Segfault in multithreaded download program - c

I am trying to write a simple program. It's supposed to read links from stdin, and download those links in seperate threads. I wrote the following code, but I am getting segmetation fault. Can anyone guess why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memcpy */
#include <curl/curl.h>
#include <pthread.h>
#define NUMTHREADS 3
struct downloadfile {
char *filename;
FILE *stream;
};
pthread_mutex_t mutex;
/* writedata: custom fwrite for curl writefunction */
static size_t writedata(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct downloadfile *out = (struct downloadfile *) stream;
if (out && !out->stream) {
out->stream = fopen(out->filename, "w");
if (!out->stream)
return -1; /* can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
/* getfilename: gets a file's name from a link. */
char *getfilename(const char *link)
{
const char *fnstart = NULL; /* start of filename*/
size_t len = 0; /* length of filename*/
for ( ; *link != '\0'; ++link) {
if (*link == '/') {
fnstart = link + 1;
len = 0;
} else {
++len;
}
}
char *filename = malloc(len + 1);
memcpy(filename, fnstart, len);
filename[len] = '\0';
return filename;
}
/* downloadthread: get a line from stdin, and try to donwload it.*/
void *downloadthread(void *ignored)
{
puts("in a download thread");
CURL *curl;
curl = curl_easy_init();
ssize_t read; /* number of characters read from a line */
if (!curl) { /* couldn't get curl handle */
fputs("Couldn't get curl handle", stderr);
pthread_exit(NULL);
}
for (;;) { /* readline and download loop */
size_t n; /* argument to getline */
char *lineptr = NULL; /* argument to getline */
struct downloadfile ofile;
/* I think I need mutex protect the getline, but I am not sure */
pthread_mutex_lock(&mutex);
read = getline(&lineptr, &n, stdin);
pthread_mutex_unlock(&mutex);
if (read == EOF)
break;
ofile.filename = getfilename(lineptr);
curl_easy_setopt(curl, CURLOPT_URL,lineptr);
/* follow http redirects */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION ,1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writedata);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ofile);
curl_easy_perform(curl);
free(ofile.filename);
free(lineptr);
if (ofile.stream)
fclose(ofile.stream);
}
curl_easy_cleanup(curl);
pthread_exit(NULL);
}
int main()
{
size_t i;
int rc;
pthread_t threads[NUMTHREADS];
curl_global_init(CURL_GLOBAL_ALL);
pthread_mutex_init(&mutex, NULL);
/* fire up threads */
for (i = 0; i < NUMTHREADS; i++) {
rc = pthread_create(&threads[i], NULL, downloadthread, NULL);
if (rc) {
printf("Error, return code from pthread is %d\n", rc);
exit(-1);
}
}
/* join all threads before cleaning up */
for (i = 0; i < NUMTHREADS; i++)
pthread_join(threads[i], NULL);
/* cleanup and exit */
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
}
Edit: Here is the output of the gdb. It didn't give much idea to me.
[New Thread 0xb61feb40 (LWP 3778)]
[New Thread 0xb57ffb40 (LWP 3779)]
[New Thread 0xb4ffeb40 (LWP 3780)]
[Thread 0xb61feb40 (LWP 3778) exited]
[Thread 0xb57ffb40 (LWP 3779) exited]
[Thread 0xb4ffeb40 (LWP 3780) exited]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7b25b40 (LWP 3773)]
0xb7e02310 in fwrite () from /lib/libc.so.6
(gdb) bt
#0 0xb7e02310 in fwrite () from /lib/libc.so.6
#1 0xb7f6dd53 in ?? () from /usr/lib/libcurl.so.4
#2 0xb7f85a5e in ?? () from /usr/lib/libcurl.so.4
#3 0xb7f86bb5 in ?? () from /usr/lib/libcurl.so.4
#4 0xb7f87573 in curl_easy_perform () from /usr/lib/libcurl.so.4
#5 0x08048d99 in downloadthread (ignored=0x0) at downloader.c:91
#6 0xb7f47ce8 in start_thread () from /lib/libpthread.so.0
#7 0xb7e874de in clone () from /lib/libc.so.6

When you declare struct downloadfile ofile, its stream field is filled with garbage and probably not 0. When ofile is then passed to writedata callback (as a result of calling curl_easy_perform), the condition out && !out->stream can thus be false and cause writedata call fwrite on unopened stream.
So just replace ofile declaration with struct downloadfile ofile = { 0, 0 };.

for ( ; *link != '\0'; ++link) {
In case the path does not contain a '/' :
for (fnstart=link ; *link != '\0'; ++link) {
of (below the loop) if (!fnstart) return BAD_STUFF;

Check the char *getfilename(const char *link) function. If the character array passed as a parameter does not contain any /, the const char *fnstart variable will stay to be NULL, you'll ultimately try to memcpy at least one byte from NULL.

Related

C Program stuck/hanging on pthread_join

Context: I've built a multi-threaded program that uses some mutexes and a semaphore to handle synchronization (attached below). The purpose of the program is to crawl the web from a seed URL until it's either exhausted all options from the seed URL or has found m valid PNG files (m is user-specified or 50 by default).
Problem: The program hangs right before the threads join when m ≥ 50, even though 50 PNGs have been found. Forcing an exit(0); call causes the program to terminate but it doesn't print out execution time which follows pthread_join() statements in main. When calling pthread_exit() instead of join, the program just hangs for values of m ≥ 50, but it terminates fine when m < 50.
Any help is greatly appreciated.
int main(int argc, char** argv) {
struct timeval starting_time;
gettimeofday(&starting_time, NULL);
get_opts(argc, argv);
hcreate(1500);
curl_global_init(CURL_GLOBAL_DEFAULT);
struct Queue* queue = createQueue(200);
/* enqueuing the first url to explore */
enqueue(queue, seed_url);
ENTRY e;
e.key = seed_url;
/* don't need a mutex here */
hsearch(e, ENTER);
pthread_t ptids[t];
/* initialize various semaphores and mutexes */
pthread_mutex_init(&m_mutex, NULL);
pthread_mutex_init(&frontier_mutex, NULL);
pthread_mutex_init(&visited_mutex, NULL);
pthread_mutex_init(&log_mutex, NULL);
/* initializing to 1 bc of the seed url */
sem_init(&filled, 0, 1);
for(int i = 0; i < t; i++) {
pthread_create(&ptids[i], NULL, crawl, queue);
}
for(int i = 0; i < t; i++) {
printf("waiting to join\n");
pthread_join(ptids[i], NULL);
}
destroyQueue(queue);
sem_destroy(&filled);
pthread_mutex_destroy(&m_mutex);
pthread_mutex_destroy(&frontier_mutex);
pthread_mutex_destroy(&visited_mutex);
pthread_mutex_destroy(&log_mutex);
hdestroy();
/* calculate running time */
struct timeval ending_time;
gettimeofday(&ending_time, NULL);
double running_time = calculate_running_time(starting_time, ending_time);
printf("findpng2 execution time: %f seconds\n", running_time);
return 0;
}
void* crawl(void * args) {
struct Queue* queue = (struct Queue *) args;
while(m > 0) {
CURL *curl_handle;
CURLcode res;
unsigned char *url = (unsigned char *) malloc(250 * sizeof(char));
RECV_BUF recv_buf;
sem_wait(&filled);
pthread_mutex_lock(&frontier_mutex);
if (isEmpty(queue)) {
pthread_mutex_unlock(&frontier_mutex);
pthread_exit(NULL);
}
// check frontiers with mutex and take first frontier
dequeue(queue, url);
if (v) {
log(url);
}
pthread_mutex_unlock(&frontier_mutex);
curl_handle = easy_handle_init(&recv_buf, url);
if ( curl_handle == NULL ) {
fprintf(stderr, "Curl initialization failed. Exiting...\n");
curl_global_cleanup();
abort();
}
/* get it! */
res = curl_easy_perform(curl_handle);
if (res == CURLE_OK) {
process_data(curl_handle, &recv_buf, NULL, queue);
}
recv_buf_cleanup(&recv_buf);
}
pthread_exit(NULL);
}
When killing the program in gdb, and running backtrace full, I get:
Thread 1 "findpng2" received signal SIGINT, Interrupt.
0x00007ffff757cd2d in __GI___pthread_timedjoin_ex (threadid=140737201288960,
thread_return=0x0, abstime=0x0, block=<optimized out>)
at pthread_join_common.c:89
89 pthread_join_common.c: No such file or directory.
(gdb) backtrace full
#0 0x00007ffff757cd2d in __GI___pthread_timedjoin_ex (
threadid=140737201288960, thread_return=0x0, abstime=0x0,
block=<optimized out>) at pthread_join_common.c:89
__tid = 25642
_buffer = {__routine = 0x7ffff757cb90 <cleanup>,
__arg = 0x7fffeee3bd28, __canceltype = 15, __prev = 0x0}
oldtype = 0
pd = 0x7fffeee3b700
self = <optimized out>
result = 0
#1 0x0000555555555d1f in main (argc=4, argv=0x7fffffffeaa8) at findpng2.c:74
i = 0
starting_time = {tv_sec = 1605405493, tv_usec = 186323}
queue = 0x5555557aaad0
e = {key = 0x7fffffffed3d "http://ece252-1.uwaterloo.ca/lab4/",
data = 0x7fffffffe9a8}
ptids = {140737201288960, 140737192896256, 140737176110848,
140737167718144, 140736951482112, 140736943089408, 140736934696704,
140736926304000, 140736917911296, 140736909518592}
ending_time = {tv_sec = 140737488349604, tv_usec = 140737488349600}
running_time = 6.9533462138242249e-310
which I find quite interesting, since the variable names after pthread_join have been populated.

main doesn't continue after call pthread_join function

I'm new to pthread and multithreading, i have written a code like that.
#include <pthread.h>
#include <unistd.h>
void *nfc_read(void *arg)
{
int fd = -1;
int ret;
uint8_t read_data[24];
while(1){
ret = read_block(fd, 8, read_data);
if(!ret){
return (void)read_data;
}
}
}
int main(int argc, char *argv[])
{
pthread_t my_thread;
void *returnValue;
pthread_create(&my_thread, NULL, nfc_read, NULL);
pthread_join(my_thread, &returnValue);
printf("NFC card value is : %s \n", (char)returnValue);
printf("other process");
return 0;
}
Until the return value from nfc_read function, as I will have other code I need to run and I don't want to block in main. How can i do that?
This is a sample where a read thread runs concurrently to the "main" thread which is doing other work (in this case, printing dots and sleeping).
To keep things simple, a simulated the reading from an input device with copying a constant string character by character. I guess, this is reasonable as the synchronization of threads is focused.
For the synchronization of threads, I used atomic_bool with atomic_store() and atomic_load() which are provided by the Atomic Library (since C11).
My sample application test-concurrent-read.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
/* sampe input */
const char sampleInput[]
= "This is sample input which is consumed as if it was read from"
" a (very slow) external device.";
atomic_bool readDone = ATOMIC_VAR_INIT(0);
void* threadRead(void *pArg)
{
char **pPBuffer = (char**)pArg;
size_t len = 0, size = 0;
int c; const char *pRead;
for (pRead = sampleInput; (c = *pRead++) > 0; sleep(1)) {
if (len + 1 >= size) {
if (!(*pPBuffer = realloc(*pPBuffer, (size + 64) * sizeof(char)))) {
fprintf(stderr, "ERROR! Allocation failed!\n");
break;
}
size += 64;
}
(*pPBuffer)[len++] = c; (*pPBuffer)[len] = '\0';
}
atomic_store(&readDone, 1);
return NULL;
}
int main()
{
/* start thread to read concurrently */
printf("Starting thread...\n");
pthread_t idThreadRead; /* thread ID for read thread */
char *pBuffer = NULL; /* pointer to return buffer from thread */
if (pthread_create(&idThreadRead, NULL, &threadRead, &pBuffer)) {
fprintf(stderr, "ERROR: Failed to start read thread!\n");
return -1;
}
/* start main loop */
printf("Starting main loop...\n");
do {
putchar('.'); fflush(stdout);
sleep(1);
} while (!atomic_load(&readDone));
putchar('\n');
void *ret;
pthread_join(idThreadRead, &ret);
/* after sync */
printf("\nReceived: '%s'\n", pBuffer ? pBuffer : "<NULL>");
free(pBuffer);
/* done */
return 0;
}
Compiled and tested with gcc in cygwin on Windows 10 (64 bit):
$ gcc -std=c11 -pthread -o test-concurrent-read test-concurrent-read.c
$ ./test-concurrent-read
Starting thread...
Starting main loop...
.............................................................................................
Received: 'This is sample input which is consumed as if it was read from a (very slow) external device.'
$
I guess, it is worth to mention why there is no mutex guarding for pBuffer which is used in main() as well as in threadRead().
pBuffer is initialized in main() before pthread_create() is called.
While thread_read() is running, pBuffer is used by it exclusively (via its passed address in pPBuffer).
It is accessed in main() again but not before pthread_join() which grants that threadRead() has ended.
I tried to find a reference by google to confirm that this procedure is well-defined and reasonable. The best, I could find was SO: pthread_create(3) and memory synchronization guarantee in SMP architectures which cites The Open Group Base Specifications Issue 7 - 4.12 Memory Synchronization.

Basic posix thread argument passing issue

I am trying to get into the world of threads and am having some trouble. The code below works every once in a while but seems to be completely random. Giving it the same input keeps giving me different results and I am quite confused.
Sometimes PrintHello() prints out the arguments and other times it prints garbage and sometimes it just segfaults.
#define NUM_THREADS 5
char *prompt = "% ";
struct thread_data{
int thread_id;
//int sum;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *local_data;
local_data = (struct thread_data *) threadarg;
int taskid = local_data->thread_id;
const char *arguments = local_data->message;
fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
pthread_exit(NULL);
}
PrintHello() is where I think the issue is.
int main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char *hp;
char *cp;
char *ofile;
int i;
int j, h, t, rc;
args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));
signal(SIGINT, SIG_IGN);
while (1) {
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, 80, stdin) == NULL)
break;
/* get rid of the '\n' from fgets */
if (line[strlen(line) - 1] == '\n'){
line[strlen(line) - 1] = '\0';
}
// split up the line
i = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
break;
args[i++] = token;
}
args[i] = NULL;
ofile = args[i-1];
printf("%s\n", ofile);
The stuff above this is just tokenizing the input and works fine.
struct thread_data thread_data_array[i];
pthread_t threads[i];
for(t=0; t<i; t++){
thread_data_array[t].thread_id = t;
thread_data_array[t].message = args[t];
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
}
}
There are several issues with your code, but I'll focus on the key ones that affect stability.
You're not checking the return values of malloc(). If the return value is NULL, it means the operation failed, and you have to either re-try, or start cleaning up all dynamically allocated memory from malloc(), calloc(), strdup(), etc, and finish up with your program gracefully. Attempting to dereference NULL (ie: use the pointer from the failed memory allocation call) will crash your program.
Your program doesn't account for zero valid arguments provided (ie: just hitting ENTER at the prompt. Change ALL newline instances to '\0', and continue.
Also, count the number of tokens you've discovered. Good practise, and helps you check if no valid input was found.
Consider reading up on starting threads in a detached state versus a joinable state. The biggest problem in your code is that you start all the threads, and then your while loop immediately executes again, and re-assigns new values to the thread[] array. Also, the same argument is passed to them while still in use (thread_data_array[t]), and you have no mutexes to protect them. Also, if your program's main() exits early, then all running threads get killed off immediately, and don't get to finish.
You should pthread_join() on joinable threads to ensure you wait until they complete before you proceed.
You provide no way to exit the program without either using CTRL+C or crashing it. Not a good idea.
Note that the threads don't necessarily execute in the order you created them, though they luckily did in this case. You'll need to learn about barriers, condition variables (condvars) and mutexes to do more advanced synchronization handling.
You're missing a lot of important header files. Surprised your code compiled.
Learn how to debug your code with gdb. In this case, I compiled it via gcc test.c -lpthread -O0 -ggdb, then stepped through the code via the "next" n command after starting it in gdb with run. It makes your life a lot easier.
Updated Code Listing
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#define NUM_THREADS 5
#define BUF_LEN (80)
char *prompt = "% ";
struct thread_data{
int thread_id;
//int sum;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *local_data;
local_data = (struct thread_data *) threadarg;
int taskid = local_data->thread_id;
const char *arguments = local_data->message;
fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
pthread_exit(NULL);
}
int main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char *hp;
char *cp;
char *ofile;
int i;
int j, h, t, rc;
args = malloc(BUF_LEN * sizeof(char *)); // ISSUE: Can fail. Check return value.
args2 = malloc(BUF_LEN * sizeof(char *)); // ISSUE: Can fail.
signal(SIGINT, SIG_IGN);
while (1)
{
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, BUF_LEN, stdin) == NULL)
{
break;
}
/* get rid of the '\n' from fgets */
/*
if (line[strlen(line) - 1] == '\n'){
line[strlen(line) - 1] = '\0';
}
*/
for ( t = 0; t < BUF_LEN; t++ )
{
if ( line[t] == '\n' )
{
line[t] = '\0';
}
}
// split up the line
i = 0;
int numTokens = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
{
break;
}
args[i++] = token;
numTokens++;
}
// Abort if zero tokens found.
if ( numTokens == 0 )
{
continue;
}
// Exit if input is "quit"
if ( strcasecmp(line, "quit") == 0 )
{
break;
}
args[i] = NULL;
ofile = args[i-1];
printf("%s\n", ofile);
struct thread_data thread_data_array[i];
pthread_t threads[i];
for(t=0; t<i; t++)
{
thread_data_array[t].thread_id = t;
thread_data_array[t].message = args[t];
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
// Wait for threads to complete work.
for(t=0; t<i; t++)
{
pthread_join(threads[t], NULL);
}
}
}
Sample Run
%
%
%
% Hello world. This is a test
test
Hello World! It's me, thread test!
Hello World! It's me, thread a!
Hello World! It's me, thread is!
Hello World! It's me, thread This!
Hello World! It's me, thread world.!
Hello World! It's me, thread Hello!
%
% 1 2 3
3
Hello World! It's me, thread 3!
Hello World! It's me, thread 2!
Hello World! It's me, thread 1!
% QUit

Does QEMU user mode emulation exit in a way that would prevent pthread_join from blocking?

I'm trying to run QEMU's user mode emulator as a thread in a larger program that I'm writing. I've modified the linux-user/main.c file so that the standard int main(int argc, char **argv, char **envp function is now called void *qemu_user_mode_func(void *arg). I've also added pthread_exit(NULL) to the end of that function, as is standard practice for pthreads (or so I've been told).
However, when I try to run a second thread that contains my own test function (shown below in void *test_func(void *arg)), the process exits before the second thread completes, even with a call to pthread_join(tid), which I've read blocks the calling thread until thread tid returns. Does QEMU's user mode emulation exit in such a way that would prevent pthread_join from exiting, or am I just using threads wrong?
Here's my code (not including the bulk of qemu_user_mode_func):
void *qemu_user_mode_func(void *arg)
{
thread_data_t *thread_data;
int argc;
char **argv;
char **envp;
/** QEMU's normal code **/
//return 0;
pthread_exit(NULL);
}
void *test_func(void *arg) {
struct timespec time;
time.tv_sec = 7;
time.tv_nsec = 0;
nanosleep(&time, NULL);
printf("hello, world - from a thread\n");
pthread_exit(NULL);
}
int main(int argc, char**argv, char **envp) {
//Initialize variables to create thread
int rc;
pthread_t threads[2];
thread_data_t main_args;
main_args.tid = 1;
main_args.argc = argc;
main_args.argv = argv;
main_args.envp = envp;
//Create thread
if ((rc = pthread_create(&(threads[0]), NULL, test_func, NULL))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
return EXIT_FAILURE;
}
if ((rc = pthread_create(&(threads[1]), NULL, qemu_user_mode_func, (void *)&main_args))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
return EXIT_FAILURE;
}
//Wait for thread to finish, then terminate process
for (rc = 0; rc < 2; rc++) {
pthread_join(threads[rc], NULL);
}
return 0;
}
EDIT: I've discovered in the void cpu_loop(CPUX86State *env) function that when the emulated program reaches its conclusion, QEMU calls syscall 231, which is sys_exit_group (as per 1). So I'm guessing this syscall is terminating the entire process that I'm running. I'd appreciate any tips on how to get around that!
If you turn a complicated preexisting application into thread there are going to be issues. One is that the application can call exit or its variants which will terminate your entire program. There are numerous other issues that could be causing a problem. I would suggest using gdb to determine what is making your program exit.
Problem was solved by editing the following section in void cpu_loop(CPUX86State *env). I capture either sys_exit_group and sys_exit system calls before they are executed, and just return from the function instead.
Original:
void cpu_loop(CPUX86State *env)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
int trapnr;
abi_ulong pc;
target_siginfo_t info;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_x86_exec(env);
cpu_exec_end(cs);
switch(trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP],
0, 0);
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* linux syscall from syscall instruction */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9],
0, 0);
break;
#endif
Modified:
void cpu_loop(CPUX86State *env)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
int trapnr;
abi_ulong pc;
target_siginfo_t info;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_x86_exec(env);
cpu_exec_end(cs);
switch(trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP],
0, 0);
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* linux syscall from syscall instruction */
----> if ((env->regs[R_EAX] == __NR_exit_group) || (env->regs[R_EAX] == __NR_exit)) {
return;
}
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9],
0, 0);
break;
#endif

How to List Active Ports and Processes using them in Linux, C Code

I am trying to write a C Code to do the same Job as:
netstat -vatp
List all Remote/Local Addresses and Processes using them. But I dunno which files should I be reading?
I tried looking into /proc/net/tcp and /proc/net/udp, but they don't have the process name or process identifier like netstat displays it!
Thanks.
You could check the source code http://freecode.com/projects/net-tools. Just download, unpack the bz2 file and you'll find the netstat.c source code
Quick analyse:
/proc/net/tcp for example has an inode tab, in /proc there is a subfolder for each of these inodes, which contains the information you need.
Some more analysing:
I think it's even worse. netstat just loops through the /proc directory and checks the contents of the numeric sub-directories to find the actual process matching the inode. Not sure as I'm just analysing
http://linux.die.net/man/5/proc is very nice reading :)
For your answer, see How can i match each /proc/net/tcp entry to each opened socket?
You could call the netstat application from within your code. Have a look at execve to capture stdout and stderr.
EDIT:
Since code says more than words:
IEFTask.h
#ifndef IEFTASK_H
#define IEFTASK_H
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* MARK: Structure */
struct IEFTask {
const char **arguments; /* last argument should be NULL */
int standardInput;
void *callbackArgument;
void (*callback)(int term, char *out, size_t outLen,
char *err, size_t errLen, void *arg);
};
typedef struct IEFTask IEFTask;
/* MARK: Running */
int
IEFTaskRun(IEFTask *theTask);
#endif /* IEFTASK_H */
IEFTask.c
#include "IEFTask.h"
/* MARK: DECLARATION: Data Conversion */
char *
IEFTaskCreateBufferFromPipe(int fd, size_t *bufLen);
/* MARK: Running */
int
IEFTaskRun(IEFTask *myTask) {
pid_t pid;
int exitStatus, status;
int outPipe[2], errPipe[2];
assert(myTask != NULL);
/* Create stdout and stderr pipes */
{
status = pipe(outPipe);
if(status != 0) {
return -1;
}
status = pipe(errPipe);
if(status != 0) {
close(errPipe[0]);
close(errPipe[1]);
return -1;
}
}
/* Fork the process and wait pid */
{
pid = fork();
if(pid < 0) { /* error */
return -1;
} else if(pid > 0) { /* parent */
waitpid(pid, &exitStatus, 0);
exitStatus = WEXITSTATUS(exitStatus);
} else { /* child */
/* close unneeded pipes */
close(outPipe[0]);
close(errPipe[0]);
/* redirect stdout, stdin, stderr */
if(myTask->standardInput >= 0) {
close(STDIN_FILENO);
dup2(myTask->standardInput, STDIN_FILENO);
close(myTask->standardInput);
}
close(STDOUT_FILENO);
dup2(outPipe[1], STDOUT_FILENO);
close(outPipe[1]);
close(STDERR_FILENO);
dup2(errPipe[1], STDERR_FILENO);
close(errPipe[1]);
execve(myTask->arguments[0],
(char *const *)myTask->arguments, NULL);
exit(127);
}
}
/* Parent continues */
{
char *output, *error;
size_t outLen, errLen;
/* 127 = execve failed */
if(exitStatus == 127) {
close(errPipe[0]);
close(errPipe[1]);
close(outPipe[0]);
close(outPipe[1]);
return -1;
}
/* Read in data */
close(errPipe[1]);
close(outPipe[1]);
output = IEFTaskCreateBufferFromPipe(outPipe[0], &outLen);
error = IEFTaskCreateBufferFromPipe(errPipe[0], &errLen);
close(errPipe[0]);
close(outPipe[0]);
/* Call callback */
(*myTask->callback)(exitStatus,
output, outLen,
error, errLen, myTask->callbackArgument);
if(output) free(output);
if(error) free(error);
}
return 0;
}
/* MARK: Data Conversion */
#define READ_BUF_SIZE (128)
char *
IEFTaskCreateBufferFromPipe(int fd, size_t *bufLen) {
ssize_t totalRead = 0, nowRead;
char readBuffer[READ_BUF_SIZE], *myBuffer = NULL;
char *ptr;
while(1) {
nowRead = read(fd, readBuffer, READ_BUF_SIZE);
if(nowRead == -1) {
free(myBuffer);
return NULL;
} else if(nowRead == 0) {
break;
} else {
ptr = realloc(myBuffer, totalRead + nowRead);
if(ptr == NULL) {
free(myBuffer);
return NULL;
}
myBuffer = ptr;
memcpy(&(myBuffer[totalRead]), readBuffer, nowRead);
totalRead += nowRead;
}
}
if(bufLen) *bufLen = (size_t)totalRead;
return myBuffer;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "IEFTask.h"
void taskCallback(int term,
char *out, size_t outlen,
char *err, size_t errlen)
{
char *ptr;
printf("Task terminated: %d\n", term);
ptr = malloc(outlen + 1);
memcpy(ptr, out, outlen);
ptr[outlen] = '\0';
printf("***STDOUT:\n%s\n***END\n", ptr);
free(ptr);
ptr = malloc(errlen + 1);
memcpy(ptr, err, errlen);
ptr[errlen] = '\0';
printf("***STDERR:\n%s\n***END\n", ptr);
free(ptr);
}
int main() {
const char *arguments[] = {
"/bin/echo",
"Hello",
"World",
NULL
};
IEFTask myTask;
myTask.arguments = arguments;
myTask.standardInput = -1;
myTask.callback = &taskCallback;
int status;
status = IEFTaskRun(&myTask);
if(status != 0) {
printf("Failed: %s\n", strerror(errno));
}
return 0;
}

Resources