In a Linux-based project that I am working on, I need to be able to find all of my child processes. It is not feasible to record every time one is started -- they need to be found after the fact. This needs to be pure C, and I'd like to do it without reading /proc. Does anyone know how to do this?
It is usually entirely feasible to record child processes every time you start one. conveniently, the parent process is passed the pid value of the child process as the return value of the fork call which creates it.
As the man page says:
pid_t fork(void);
It would help if you could tell us why you think it isn't feasible.
I find your comment that it is not feasible to record the creation of processes to be odd, but if you really can't (possibly because you don't know how many will be created and don't want to have to keep reallocing memory), then I would probably open all of the files that match the glob /proc/[1-9]*/status and look for the line that says PPid: <num> where <num> was my process id.
You could use popen
Something like. (Hopefully the syntax is close enough)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp = popen("ps -C *YOUR PROGRAM NAME HERE* --format '%P %p'" , "r");
if (fp == NULL)
{
printf("ERROR!\n");
}
char parentID[256];
char processID[256];
while (fscanf(fp, "%s %s", parentID, processID) != EOF)
{
printf("PID: %s Parent: %s\n", processID, parentID);
// Check the parentID to see if it that of your process
}
pclose(fp);
return 1;
}
You could try this
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
char str[50] = "ps -o pid --ppid ";
char ppid [7];
sprintf(ppid,"%d",getpid());
strcat(str,ppid);
system(str);
NOTE: This piece of code needs to be in the parent process
Basically ps -o pid --ppid <parent_id> gives the pid of all child processes whose parent has PID <parent_id>. Now, we can get the parent's process's PID by using getpid(), which returns pid_t and is implicitly converted to an integer. sprintf() converts it into a string and we concatenate the result with str to get the complete command which is executed by system().
You could parse a process list (ps -ax?) that included the parent process ID. This could probably be done with a simple shell script.
If you're trying to get all child processes for the very specific purpose of waiting for them to exit, you can use waitpid(-1, ...):
while (true) {
// Wait for any child exiting
int child_status;
const int child_pid = waitpid(-1, &child_status, 0);
// check child_status
}
If you want to trace fork events and extract child pids for debugging purposes, there are a number of ways to do that, including:
Using GDB
using strace
Using systemtap
Using kernel event connectors (not sure what these are exactly)
The code in this repository will need to be adjusted to be written in plain C to meet your request, and if you are ok with doing so, if you remove all the unused functionality from my repository within your project, it shouldnt be that hard to translate into plain C.
But this is more useful for people looking to support multiple platforms and/or who prefer C++.
See the function pids_from_ppid(ppid)
https://github.com/time-killer-games/enigma-dev/blob/548dc16e96a2a32f8ad9045a4ee18b0206516e62/ENIGMAsystem/SHELL/Universal_System/Extensions/ProcInfo/procinfo.h#L101
Returns a string with each child process id separated by a pipe "|" character as the delimiter.
Ubuntu and debian users need libprocps-dev installed for missing headers.
sudo apt-get install libprocps-dev libx11-dev
The libx11-dev dependency is optional because all X11 code in the source can be omitted and the question here will still be answered, so if you need wayland and no X11 support you should remove X11 related code as it's unrelated to this question anyway.
For those who actually like/need X11 and/or C++ this will work out of the box on Windows, Mac, Linux, and FreeBSD. Support for other BSD's is not as easily viable thanks to relying on the libutil dependency. But it just uses sysctl() internally so in theory you should be able to compile the source code for libutil on the other BSD's available from FreeBSD's github repository, and then link to it after you have built it.
Related
I would like to open an bash application(prog1) and send command to that application with C program.
I tried and wrote the following code.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
FILE * fp;
char message[10] = "command1\n";
fp = popen("sudo prog1","r");
write(STDIN_FILENO, message, 10);
pclose(fp);
execlp("sudo prog1", "sudo prog1", "-l", NULL);
return 0;
}
This code gives an output:
Linux:~$ ./ prog 2 // running the code
command // prints "command"
prog1-> // Then opens "prog1" (prog1 waits for user commands)
But I want it to be
Linux:~$ ./ prog 2 // running the code
prog1-> command // open "prog1" and write command (instead of getting it from user)
It either writes after quitting prog1 or before starting prog1. Please let me know how to write the "command" in prog1, after opening the application prog1. Thank you.
Note: If I make
fp = popen("sudo prog1","w"); //to write
It throws the following error
tcgetattr : Inappropriate ioctl for device
Your main bug is thinking that popen() somehow associates your child process with your STDIN_FILENO. It doesn't. STDIN_FILENO is not associated with your "sudo prog1" child. You'd have to create a pipe, dup the descriptors to stdin/stdout, and fork+exec to do that. But you used popen() so don't do that either.
Instead, you should be writing and reading from fp.
Something like:
fprintf(fp, message);
fgets(response, 100, fp);
Since fprintf() is buffered, you should use \n at the end of the line, or fflush().
Also, there is no point is using exec/execlp at the end when you've already called popen(). Looks like you may be mixing two approaches that you've seen by example.
popen() essentially does a combination of (pipe, dup stdin/stdout/stderr, fork, execl) to take care of redirecting a child process to a file stream connected to the parent. So no need to reimplement unless you need different semantics than popen().
You technically are implementing "expect" functionality, so you might want to look into expect, or expect modules for different languages. Expect is included with Linux distributions but is usually optional.
http://expect.sourceforge.net/
http://search.cpan.org/~rgiersig/Expect-1.21/Expect.pod
And not to mention, Perl has a Sudo module already.
http://search.cpan.org/~landman/Sudo-0.21/lib/Sudo.pm
I am packaging a program on Windows that expects to be able to externally call OpenSSH. So, I need to package ssh.exe with it and I need to force ssh.exe to always be called with a custom command line parameter (specifically -F to specify a config file it should use). There is no way to force the calling program to do this, and there are no simple ways to do this otherwise in Windows (that I can think of anyway - symlinks or cmd scripts won't work) so I was just going to write a simple wrapper in C to do it.
This is the code I put together:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ret;
char **newv = malloc((argc + 2) * sizeof(*newv));
memmove(newv, argv, sizeof(*newv) * argc);
newv[argc] = "-F ssh.conf";
newv[argc+1] = 0;
ret = execv("ssh.orig.exe", newv);
printf("execv failed with return value of %i", ret);
return ret;
}
I then compile this code using GCC 4.6.3 in Cygwin and it runs without error; however, there is a strange behavior with regards to input and output. When you go to type at the console (confirming the authenticity of the host and entering in a password, etc) only part of the input appears on the console. For example, if I type in the word 'yes' and press enter, only the 'e' will appear on the console and SSH will display an error about needing to type 'yes' or 'no'. Doing this from the Windows command prompt will result in your input going back to the command propmt, so when you type 'yes' and press enter, you get the ''yes' is not recognized as an internal or external command...' message as if the input were being typed at the command prompt. Eventually SSH will time out after that.
So, I'm obviously missing something here, and I'm assuming it has something to do with the way execv works (at least the POSIX Cygwin version of it).
Is there something I'm missing here or are there any alternatives? I was wondering if maybe I need to fork it and redirect the I/O to the fork (although fork() doesn't seem to work - but there are other issues there on Windows). I tried using _execv from process.h but I was having issues getting the code right for that (also could have been related to trying to use gcc).
It's also possible that there may be a non-programming way to do this that I haven't thought of, but all of the possibilities I've tried don't seem to work.
Thoughts?
I ended up finding a solution to this problem. I'm sure there were other ways to do this, but this seems to fix the issue and works well. I've replaced the execv line with the following code:
ret = spawnv(P_WAIT, "ssh.orig.exe", newv);
You have to use 'P_WAIT' otherwise the parent process completes and exits and you still have the same problem as before. This causes the parent process to wait, but still transfers input and output to the child process.
I am attempting to run a very simple program with fork and execlp, but it does not work as I would expect.
I presently have a file in my working directory simply named '1'. So the command rm 1* should remove it. However when tried via execlp, it does not.
int main()
{
if(fork()==0)
{
execlp("rm", "rm", "1*", NULL);
perror("Problem\n");
}
return 0;
}
Thank you.
For what you're trying to do, you want:
execlp("sh", "sh", "rm 1*", (char *)0);
Note that this is a rather bad idea, from the standpoints of security, robustness, and efficiency. If you want to remove files matching a pattern, you should do this directly in C. It's easy with the glob function and a simple loop.
Using system() or exec(), I can get any command to execute but they display the result into the Console. I want to execute the command and extract the output and process it, and then display it. How can I achieve this on Windows/DOS Platform?
There's nothing like that in standard C, but usually, for compatibility with POSIX, compilers implement popen (_popen in VC++), which starts a command (as it would do with system) and returns a FILE * to the stream you asked for (you can ask for stdout if you want to read the output or stdin if you want to give some input to the program).
Once you have the FILE *, you can read it with the usual fread/fscanf/..., like you would do on a regular file.
If you want to have both input and output redirection things start to get a bit more complicated, since Windows compilers usually do have something like POSIX pipe, but it isn't perfectly compatible (mostly because the Windows process creation model is different).
In this case (and in any case where you need more control on the started process than the plain popen gives you) I would simply go with the "real" way to perform IO redirection on Windows, i.e. with CreateProcess and the appropriate options; see e.g. here.
Matteo Italia's answer is awesome. But some compilers (especially older ones) don't support popen().
If popen() is not supported, here's a possible solution.
In DOS we can redirect the output to a temporary file using >.
Ex:
C:\> ipconfig > temp.txt
I can then open temp.txt in my C-code and then reprocess its content.
My C code for this will be something like:
system("ipconfig > temp.txt");
FILE *fp;
fp = fopen("temp.txt","r");
// //
//... Code for reprocessing can go here ...//
// //
Here's an alternative answer for those without popen that should work on most system. This code is not thread safe. I expect that this is not a significant limitation for most situation.
#include <stdio.h>
#include <process.h>
#include <io.h>
#include <sys\stat.h>
#include <assert.h>
int main()
{
int so = dup(1);
close(1);
int i = creat("output.txt", S_IWRITE);
assert(i == 1); // i should be 1...
system("dir");
close(i);
dup2(so, 1);
close(so);
}
my code is as follows: preload.c, with the following content:
#include <stdio.h>
#include <stdlib.h>
int __attribute__((constructor)) main_init(void)
{
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
FILE *fp = popen("ls", "r");
pclose(fp);
}
then in the shell (do the 2nd command with care!!):
gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC
LD_PRELOAD=./mylib.so bash
!!! be carefull with the last command it will result with endless loop of forking "sh -c ls". Stop it after 2 seconds with ^C, (or better ^Z and then see ps).
More info
This problem relate to bash in some way; either as the command that the user run, or as the bash the popen execute.
additional Key factors: 1) perform the popen from the pre-loaded library, 2) probably need to do the popen in the initialization section of the library.
if you use:
LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash
instead of the last command, you will get many ld-debug files, named /tmp/ld-debug.*. One for each forked process. IN ALL THESE FILES you'll see that symbols are first searched in mylib.so even though LD_PRELOAD was removed from the environment.
edit: so the problem/question actually was: howcome can't you unset LD_PRELOAD reliably using a preloaded main_init() from within bash.
The reason is that execve, which is called after you popen, takes the environment from (probably)
extern char **environ;
which is some global state variable that points to your environment. unsetenv() normally modifies your environment and will therefore have an effect on the contents of **environ.
If bash tries to do something special with the environment (well... would it? being a shell?) then you may be in trouble.
Appearantly, bash overloads unsetenv() even before main_init(). Changing the example code to:
extern char**environ;
int __attribute__((constructor)) main_init(void)
{
int i;
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD"));
printf("Environ: %lx\n",environ);
printf("unsetenv: %lx\n",unsetenv);
for (i=0;environ[i];i++ ) printf("env: %s\n",environ[i]);
fflush(stdout);
FILE *fp = popen("ls", "r");
pclose(fp);
}
shows the problem. In normal runs (running cat, ls, etc) I get this version of unsetenv:
unsetenv: 7f4c78fd5290
unsetenv: 7f1127317290
unsetenv: 7f1ab63a2290
however, running bash or sh:
unsetenv: 46d170
So, there you have it. bash has got you fooled ;-)
So just modify the environment in place using your own unsetenv, acting on **environ:
for (i=0;environ[i];i++ )
{
if ( strstr(environ[i],"LD_PRELOAD=") )
{
printf("hacking out LD_PRELOAD from environ[%d]\n",i);
environ[i][0] = 'D';
}
}
which can be seen to work in the strace:
execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0
Q.E.D.
(The answer is a pure speculation, and may be is incorrect).
Perhaps, when you fork your process, the context of the loaded libraries persists. So, mylib.so was loaded when you invoked the main program via LD_PRELOAD. When you unset the variable and forked, it wasn't loaded again; however it already has been loaded by the parent process. Maybe, you should explicitly unload it after forking.
You may also try to "demote" symbols in mylib.so. To do this, reopen it via dlopen with flags that place it to the end of the symbol resolution queue:
dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);
the answer from mvds is incorrect!
popen() will spawn child process which inherit the preloaded .so lied in parent process. this child process don't care LD_PRELOAD environment.