I am referring to this link,
Basically, consider the input happy'; useradd 'attacker, the security advice differentiates between a compliant and non-compliant code -
Non Complaint Code
#include <string.h>
#include <stdlib.h>
enum { BUFFERSIZE = 512 };
void func(const char *input) {
char cmdbuf[BUFFERSIZE];
int len_wanted = snprintf(cmdbuf, BUFFERSIZE,
"any_cmd '%s'", input);
if (len_wanted >= BUFFERSIZE) {
/* Handle error */
} else if (len_wanted < 0) {
/* Handle error */
} else if (system(cmdbuf) == -1) {
/* Handle error */
}
}
Compliant Code
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void func(char *input) {
pid_t pid;
int status;
pid_t ret;
char *const args[3] = {"any_exe", input, NULL};
char **env;
extern char **environ;
/* ... Sanitize arguments ... */
pid = fork();
if (pid == -1) {
/* Handle error */
} else if (pid != 0) {
while ((ret = waitpid(pid, &status, 0)) == -1) {
if (errno != EINTR) {
/* Handle error */
break;
}
}
if ((ret != -1) &&
(!WIFEXITED(status) || !WEXITSTATUS(status)) ) {
/* Report unexpected child status */
}
} else {
/* ... Initialize env as a sanitized copy of environ ... */
if (execve("/usr/bin/any_cmd", args, env) == -1) {
/* Handle error */
_Exit(127);
}
}
}
Assume we pass the same input to both the function with equal privilege, i.e run by root etc etc, How does the second solution ensure that command injection attack is repelled?
My only guess is that, execve will refresh your binary image with any_cmdand use input happy'; useradd 'attacker as args to any_cmd. So we will be have a return value equivalent to "invalid parameters". Is my understanding right? Or is there something deeper than my understanding which I am missing?
The main difference is indeed that with the system function you can launch whatever your shell can execute, so you basically can have shell injections with multiple commands. Whereas with execve first you specify a specific binary to execute, so you're pretty much sure that there is only one command executed (except if you execve a shell..). Also since you give a complete path to execve you avoid hacks based on modifying the HOME or the current working directory.
So yes, your understanding is rather right
Related
I am tracing some processes and their children using ptrace. I am trying to print specific system call (using Seccomp filter that notifies ptrace, see this blogpost).
In most cases my code (see below) is working fine. However, when I am tracing a java program (from the default-jre package), the latter clones using the CLONE_THREAD flag. And for some reason, my tracer hangs (I believe) because I can't receive signals from the cloned process. I think the reason is that (according to this discussion) the child process in fact becomes a child of the original process' parent, instead of becoming the original process' child.
I reproduced this issue by using a simple program that simply calls clone() with flags and perform actions. When I used the when I use CLONE_THREAD | CLONE_SIGHAND | CLONE_VM flags (as clone() documentation specifies they should come together since Linux 2.6.0), at least I am able to trace everything correctly until one of the two thread finishes.
I would like to trace both thread independently. Is it possible?
More importantly, I need to trace a Java program, and I cannot change it. Here a strace of the Java program clone call:
[...]
4665 clone(child_stack=0x7fb166e95fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[4666], tls=0x7fb166e96700, child_tidptr=0x7fb166e969d0) = 4666
[...]
So Java seems to respect the rules. I wanted to experiment to understand: I ruled out any flags unrelated to thread (i.e., `CLONE_FS | CLONE_FILES | CLONE_SYSVSEM).
Here are the results of running my test program with different combination of flags (I know, I am really desperate):
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID: inconsistent; gets trace from both until the parent finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_CHILD_CLEARTID: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID|CLONE_PARENT_SETTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID:
only gets trace from parent
So at least I get the same behaviour from my program and the Java program: it does not work.
How can I make it work? For instance, how does strace successfully traces any kind of clone? I tried to dig into its code but I can't find how they are doing it.
Any help might appreciated!
Best regards,
The tracer code (compile with g++ tracer.cpp -o tracer -g -lseccomp -lexplain):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stddef.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <libexplain/waitpid.h>
#include <tuple>
#include <vector>
#define DEFAULT_SIZE 1000
#define MAX_SIZE 1000
int process_signals();
int inspect(pid_t);
void read_string_into_buff(const pid_t, unsigned long long, char *, unsigned int);
int main(int argc, char **argv){
pid_t pid;
int status;
if (argc < 2) {
fprintf(stderr, "Usage: %s <prog> <arg1> ... <argN>\n", argv[0]);
return 1;
}
if ((pid = fork()) == 0) {
/* If execve syscall, trace */
struct sock_filter filter[] = {
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_getpid, 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short) (sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
ptrace(PTRACE_TRACEME, 0, 0, 0);
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)");
return 1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("when setting seccomp filter");
return 1;
}
kill(getpid(), SIGSTOP);
return execvp(argv[1], argv + 1);
} else {
waitpid(pid, &status, 0);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP | PTRACE_O_TRACEFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEVFORK );
ptrace(PTRACE_CONT, pid, 0, 0);
process_signals();
return 0;
}
}
int process_signals(){
int status;
while (1){
pid_t child_pid;
// When child status changes
if ((child_pid = waitpid(-1, &status, 0)) < 0){
fprintf(stderr, "%s\n", explain_waitpid(child_pid, &status, 0));
exit(EXIT_FAILURE);
}
//printf("Sigtrap received\n");
// Checking if it is thanks to seccomp
if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))){
// Perform argument inspection with ptrace
int syscall = inspect(child_pid);
}
// Resume no matter what
ptrace(PTRACE_CONT, child_pid, 0, 0);
}
}
int inspect(pid_t pid){
printf("From PID: %d\n", pid);
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, 0, ®s);
// Get syscall number
int syscall = regs.orig_rax;
printf("------\nCaught syscall: %d\n", syscall);
if (syscall == __NR_getpid){
printf("Getpid detected\n");
}
return syscall;
}
void read_string_into_buff(const pid_t pid, unsigned long long addr, char * buff, unsigned int max_len){
/* Are we aligned on the "start" front? */
unsigned int offset=((unsigned long)addr)%sizeof(long);
addr-=offset;
unsigned int i=0;
int done=0;
int word_offset=0;
while( !done ) {
unsigned long word=ptrace( PTRACE_PEEKDATA, pid, addr+(word_offset++)*sizeof(long), 0 );
// While loop to stop at the first '\0' char indicating end of string
while( !done && offset<sizeof(long) && i<max_len ) {
buff[i]=((char *)&word)[offset]; /* Endianity neutral copy */
done=buff[i]=='\0';
++i;
++offset;
}
offset=0;
done=done || i>=max_len;
}
}
The sample program (compile with gcc sample.c -o sample):
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#define FLAGS CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
int fn(void *arg)
{
printf("\nINFO: This code is running under child process.\n");
int i = 0;
int n = atoi(arg);
for ( i = 1 ; i <= 10 ; i++ )
printf("[%d] %d * %d = %d\n", getpid(), n, i, (n*i));
printf("\n");
return 0;
}
void main(int argc, char *argv[])
{
printf("[%d] Hello, World!\n", getpid());
void *pchild_stack = malloc(1024 * 1024);
if ( pchild_stack == NULL ) {
printf("ERROR: Unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
int pid = clone(fn, pchild_stack + (1024 * 1024), FLAGS, argv[1]);
if ( pid < 0 ) {
printf("ERROR: Unable to create the child process.\n");
exit(EXIT_FAILURE);
}
fn(argv[1]);
wait(NULL);
free(pchild_stack);
printf("INFO: Child process terminated.\n");
}
You can test what you want by running ./tracer ./sample. You can also test the original test case ./tracer java and observe that both the tracer and java hangs.
ANSWER:
As pointed it out in the comment, I had issues in that example that were preventing me from handling signals from the child.
In my original code (not listed here because too complex), I was only attaching ptrace AFTER the processes started... and I was only attaching to PID listed by pstree. My mistake was that I omitted the threads (and java is one program that does create threads), explaining why I had issue tracing java only.
I modified the code to attach to all the children process and thread (ps -L -g <Main_PID> -o tid=) and everything works again.
Your sample program has a bug: it may free the second thread’s stack before that thread exits, causing a SEGV. And your tracer just doesn’t handle signals well.
If the traced program gets a signal, your tracer intercepts it, not passing it down to the program. When it continues the program, it continues from the very same operation that caused SEGV, so it gets SEGV again. Ad infinitum. Both the tracer and the tracee appear to hang but in fact, they are in an infinite loop.
Rewriting the continuation like the following seems to work:
if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))){
// Perform argument inspection with ptrace
int syscall = inspect(child_pid);
ptrace(PTRACE_CONT, child_pid, 0, 0);
} else if (WIFSTOPPED(status)) {
ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
} else {
ptrace(PTRACE_CONT, child_pid, 0, 0);
}
Not sure of Java but it seems to get SEGVs in regular operation...
I'm doing some practice on process management in Linux and how to use system calls and communication between child and parent processes. I need to implement a pipe to get the string provided by child process, which is the directory list as string and pass it to the parent process to count the number of lines in that string and find the number of files in that directory by doing that. The problem i faced is here:
error: initializer fails to determine size of ‘dirFileList’
char dirFileList[] = read(tunnel[0],buf,MAX_BUF)
Also my code is down below:
#define die(e) do { fprintf(stderr, "%s\n", e); exit(EXIT_FAILURE); } while (0);
#define MAX_BUF 2024
int main()
{
const char *path = (char *)"/"; /* Root path */
const char *childCommand = (char *)"ls |"; /* Command to be executed by the child process */
const char *parentCommand = (char *)"wc -l"; /* Command to be executed by the parent process */
int i = 0; /* A simple loop counter :) */
int counter = 0; /* Counts the number of lines in the string provided in the child process */
int dirFileNum; /* Keeps the list of files in the directory */
int tunnel[2]; /* Defining an array of integer to let the child process store a number and parent process to pick that number */
pid_t pID = fork();
char buf[MAX_BUF]; /* Fork from the main process */
if (pipe(tunnel) == -1) /* Pipe from the parent to the child */
die("pipe died.");
if(pID == -1) /* Check if the fork result is valid */
{
die("fork died.");
}
else if(pID == 0) /* Check if we are in the child process */
{
dup2 (tunnel[1], STDOUT_FILENO); /* Redirect standard output */
close(tunnel[0]);
close(tunnel[1]);
execl(childCommand, path); /* Execute the child command */
die("execl died.");
}
else /* When we are still in the main process */
{
close(tunnel[1]);
char dirFileList[] = read(tunnel[0],buf,MAX_BUF); /* Read the list of directories provided by the child process */
for(i;i<strlen(dirFileList);i++) /* Find the number of lines in the list provided by the child process */
if(dirFileList[i] == '\n')
counter++;
printf("Root contains %d files.", counter); /* Print the result */
wait(NULL); /* Wait until the job is done by the child process */
}
return 0;
}
If you'd shown us the whole error message, we'd see it's referring to this line:
char dirFileList[] = read(tunnel[0],buf,MAX_BUF);
You can't declare an indeterminate array like that. And if you read the man page of read(2), you'll see that the return value is
On success, the number of bytes read ...
On error, -1 ...
So you want something like
int bytes_read = read(...);
if (bytes_read < 0) {
perror("read");
exit(1);
}
Some additional review (which you didn't ask for, but may be instructive):
Don't cast string literals to char*, especially when you're then assigning to const char* variables.
Instead of just printing a fixed message on error, you can be more informative after a call that has set errno if you use perror() - see my sample above.
die() could be implemented as a function, which will make it easier to debug and to use correctly than a macro.
I am forking a process and running a wc command using execl. Now under correct arguments, it runs fine, but when I give a wrong file name, it fails, but in both the cases the return value of
WEXITSTATUS(status)
is always 0.
I believe there is something wrong with what I am doing, but I'm not sure what is. Reading man pages and Google suggests that I should get a correct value as per the status code.
Here is my code:
#include <iostream>
#include <unistd.h>
int main(int argc, const char * argv[])
{
pid_t pid = fork();
if(pid <0){
printf("error condition");
} else if(pid == 0) {
printf("child process");
execl("/usr/bin/wc", "wc", "-l", "/Users/gabbi/learning/test/xyz.st",NULL);
printf("this happened");
} else {
int status;
wait(&status);
if( WIFEXITED( status ) ) {
std::cout << "Child terminated normally" << std::endl;
printf("exit status is %d",WEXITSTATUS(status));
return 0;
} else {
}
}
}
If you supply a name of non existing file to execl() as 1st argument it fails. If this happens the program leaves without returning any specifiy value. So the default of 0 is returned.
You could fix the for example like this:
#include <errno.h>
...
int main(int argc, const char * argv[])
{
pid_t pid = fork();
if(pid <0){
printf("error condition");
} else if(pid == 0) {
printf("child process");
execl(...); /* In case exec succeeds it never returns. */
perror("execl() failed");
return errno; /* In case exec fails return something different then 0. */
}
...
You are not passing the file name from argv to the child process
Instead of
execl("/usr/bin/wc", "wc", "-l", "/Users/gabbi/learning/test/xyz.st",NULL);
Try this,
execl("/usr/bin/wc", "wc", "-l", argv[1],NULL);
The output I got on my machine
xxx#MyUbuntu:~/cpp$ ./a.out test.txt
6 test.txt
Child terminated normally
exit status is 0
xxx#MyUbuntu:~/cpp$ ./a.out /test.txt
wc: /test.txt: No such file or directory
Child terminated normally
exit status is 1
This was an xcode issue, running from console works fine. I am a Java guy, doing some assignments in CPP. Nevertheless, it might come handy to someone getting stuck at similar issue.
Using Linux and C++, I would like a function that does the following:
string f(string s)
{
string r = system("foo < s");
return r;
}
Obviously the above doesn't work, but you get the idea. I have a string s that I would like to pass as the standard input of a child process execution of application "foo", and then I would like to record its standard output to string r and then return it.
What combination of Linux syscalls or POSIX functions should I use?
I'm using Linux 3.0 and do not need the solution to work with older systems.
The code provided by eerpini does not work as written. Note, for example, that the pipe ends that are closed in the parent are used afterwards. Look at
close(wpipefd[1]);
and the subsequent write to that closed descriptor. This is just transposition, but it shows this code has never been used. Below is a version that I have tested. Unfortunately, I changed the code style, so this was not accepted as an edit of eerpini's code.
The only structural change is that I only redirect the I/O in the child (note the dup2 calls are only in the child path.) This is very important, because otherwise the parent's I/O gets messed up. Thanks to eerpini for the initial answer, which I used in developing this one.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
int aStdinPipe[2];
int aStdoutPipe[2];
int nChild;
char nChar;
int nResult;
if (pipe(aStdinPipe) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(aStdoutPipe) < 0) {
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
nChild = fork();
if (0 == nChild) {
// child continues here
// redirect stdin
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
exit(errno);
}
// redirect stdout
if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
exit(errno);
}
// redirect stderr
if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// run child process image
// replace this with any exec* function find easier to use ("man exec")
nResult = execve(szCommand, aArguments, aEnvironment);
// if we get here at all, an error occurred, but we are in the child
// process, so just exit
exit(nResult);
} else if (nChild > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(aStdinPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// Include error check here
if (NULL != szMessage) {
write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
}
// Just a char by char read here, you can change it accordingly
while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// done with these in this example program, you would normally keep these
// open of course as long as you want to talk to the child
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
} else {
// failed to create child
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
}
return nChild;
}
Since you want bidirectional access to the process, you would have to do what popen does behind the scenes explicitly with pipes. I am not sure if any of this will change in C++, but here is a pure C example :
void piped(char *str){
int wpipefd[2];
int rpipefd[2];
int defout, defin;
defout = dup(stdout);
defin = dup (stdin);
if(pipe(wpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(pipe(rpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(dup2(wpipefd[0], 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(rpipefd[1], 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(fork() == 0){
close(defout);
close(defin);
close(wpipefd[0]);
close(wpipefd[1]);
close(rpipefd[0]);
close(rpipefd[1]);
//Call exec here. Use the exec* family of functions according to your need
}
else{
if(dup2(defin, 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(defout, 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
close(defout);
close(defin);
close(wpipefd[1]);
close(rpipefd[0]);
//Include error check here
write(wpipefd[1], str, strlen(str));
//Just a char by char read here, you can change it accordingly
while(read(rpipefd[0], &ch, 1) != -1){
write(stdout, &ch, 1);
}
}
}
Effectively you do this :
Create pipes and redirect the stdout and stdin to the ends of the two pipes (note that in linux, pipe() creates unidirectional pipes, so you need to use two pipes for your purpose).
Exec will now start a new process which has the ends of the pipes for stdin and stdout.
Close the unused descriptors, write the string to the pipe and then start reading whatever the process might dump to the other pipe.
dup() is used to create a duplicate entry in the file descriptor table. While dup2() changes what the descriptor points to.
Note : As mentioned by Ammo# in his solution, what I provided above is more or less a template, it will not run if you just tried to execute the code since clearly there is a exec* (family of functions) missing, so the child will terminate almost immediately after the fork().
Ammo's code has some error handling bugs. The child process is returning after dup failure instead of exiting. Perhaps the child dups can be replaced with:
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
)
{
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
How can I get the PID of a service called abc using C++ on Linux without using a system call? I would appreciate any examples that you care to offer.
Since use of sysctl has been discouraged for ages now, the recommended way of doing this is by examining each of the process entries in /proc and reading the comm file in each folder. If, for your example, the contents of that file are abc\n, that's the process you're looking for.
I don't really speak C++, but here's a possible solution in POSIX C89:
#include <glob.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
pid_t find_pid(const char *process_name)
{
pid_t pid = -1;
glob_t pglob;
char *procname, *readbuf;
int buflen = strlen(process_name) + 2;
unsigned i;
/* Get a list of all comm files. man 5 proc */
if (glob("/proc/*/comm", 0, NULL, &pglob) != 0)
return pid;
/* The comm files include trailing newlines, so... */
procname = malloc(buflen);
strcpy(procname, process_name);
procname[buflen - 2] = '\n';
procname[buflen - 1] = 0;
/* readbuff will hold the contents of the comm files. */
readbuf = malloc(buflen);
for (i = 0; i < pglob.gl_pathc; ++i) {
FILE *comm;
char *ret;
/* Read the contents of the file. */
if ((comm = fopen(pglob.gl_pathv[i], "r")) == NULL)
continue;
ret = fgets(readbuf, buflen, comm);
fclose(comm);
if (ret == NULL)
continue;
/*
If comm matches our process name, extract the process ID from the
path, convert it to a pid_t, and return it.
*/
if (strcmp(readbuf, procname) == 0) {
pid = (pid_t)atoi(pglob.gl_pathv[i] + strlen("/proc/"));
break;
}
}
/* Clean up. */
free(procname);
free(readbuf);
globfree(&pglob);
return pid;
}
Caveat: if there are multiple running processes with the name you're looking for, this code will only return one. If you're going to change that, be aware that with the naive glob written, you'll also examine /proc/self/comm, which could potentially lead to a duplicate entry.
If there are multiple processes with the same name, there isn't really a way to ensure you got the right one. Many daemons have the ability to save their pids to a file for this reason; check your documentation.
Google has this covered :)
http://programming-in-linux.blogspot.com/2008/03/get-process-id-by-name-in-c.html
Although it does use sysctl, which is a system call!
It's C but should work just as well in C++