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.
Related
Can't seem to make any progress on this C assignment, hopefully somebody immediately spots the problem: Goal is to implement a thread pool using a POSIX message queue. The threads are created at start and then receive task after task from the message queue. The threads are expected to receive a 16 byte struct of type task_t that contains the function as well as a pointer to the arguments it should be executed with.
I get a segmentation fault as soon as the first task is being worked on by a thread, i.e. when executing function foo which can be accessed in threadPoolFun as task_ptr->func and is supposed to be run with argument task_ptr->this:
void * threadPoolFun(void * arg) {
task_t * task_ptr;
int size = attr.mq_msgsize; //size = 16
char buf[size];
while (1) {
int ret_val = mq_receive(task_queue, buf, size, NULL);
if (ret_val == -1) {
perror("Error receiving message: ");
}
task_ptr = (task_t *) buf;
if (task_ptr->func == NULL) {
break;
}
task_ptr->func(task_ptr->this);
}
return NULL;
}
Segmentation fault:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040086a in foo (arg=0x7ffdd71fdfd0) at all_in_one.c:37
37 td->retVal = (double) td->tid;
[Current thread is 1 (Thread 0x7fec2d544700 (LWP 9853))]
(gdb) list
32 fooTask_t; // type for this tasks
33
34 //definition of task: write tid to retVal
35 void foo(void * arg) {
36 fooTask_t *td = (fooTask_t *) arg;
37 td->retVal = (double) td->tid;
38 }
39
40 // initialize task
41 void fooInit(fooTask_t * t) {
(gdb) backtrace
#0 0x000000000040086a in foo (arg=0x7ffdd71fdfd0) at all_in_one.c:37
#1 0x0000000000400b8c in threadPoolFun (arg=0x0) at all_in_one.c:118
#2 0x00007fec2f1196ba in start_thread (arg=0x7fec2d544700)
at pthread_create.c:333
#3 0x00007fec2ee4f41d in clone ()
Full code:
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
//number of threads
#define NTH 4
//number of tasks
#define NTASKS 10
void * threadPoolFun(void * arg);
typedef void( * taskfun_t)(void * );
//minimal task data structure: func to be executed with args this, abstraction from actual task
typedef struct minTaskDataStruct {
void * this;
taskfun_t func;
}
task_t;
//data structure for actual task
typedef struct fooDataStruct {
// mandatory entries
void * this; // pointer to this structure
taskfun_t func; // function with signature: void foo(void *)
// data for individual task instances
long tid; // task id
double retVal; // return value
}
fooTask_t; // type for this tasks
//definition of task: write tid to retVal
void foo(void * arg) {
fooTask_t *td = (fooTask_t *) arg;
td->retVal = (double) td->tid;
}
// initialize task
void fooInit(fooTask_t * t) {
t-> this = t; // set this pointer
t-> func = foo; // set task function
}
//data structure for threads
pthread_t th[NTH];
//data structure for task queue attributes
static struct mq_attr attr;
//task queue
mqd_t task_queue;
//create task structs and store them in array td
fooTask_t td[NTASKS];
int main() {
printf("Setting up tasks\n");
for (int i = 0; i < NTASKS; i++) {
fooTask_t task;
task.tid = i;
fooInit(&task);
td[i] = task;
}
// set attributes
attr.mq_flags = 0; /* Flags: 0 or O_NONBLOCK */
attr.mq_maxmsg = 10; /* Max. # of messages on queue */
attr.mq_msgsize = 16; /* Max. message size (bytes) */
printf("Opening task queue\n");
// set up task queue
task_queue = mq_open("/my_task_queue_mq", O_CREAT | O_RDWR, 0700, & attr);
//create threads with default attributes, pass threadpool function
//threads will run as long as func passed to them is not NULL
for (long i = 0; i < NTH; i++) {
printf("Creating thread %ld\n", i);
pthread_create( & th[i], NULL, threadPoolFun, NULL);
}
//send tasks to queue, tasks to be consumed by threads, only send first 16 bytes
for (int i = 0; i < NTASKS; i++) {
printf("Sending task %d\n", i);
mq_send(task_queue, (const char * ) &td[i], sizeof(task_t), 0);
}
//send dummy tasks with func==NULL to terminate threads
for (int i = 0; i < NTASKS; i++) {
printf("Sending dummy task %d\n", i);
task_t dummy_task;
dummy_task.this = & dummy_task;
dummy_task.func = NULL;
mq_send(task_queue, (const char * ) & dummy_task, sizeof(task_t), 0);
}
//verify task execution
int sum1 = 0;
int sum2 = 0;
for (int i = 0; i < NTASKS; i++) {
sum1 += td[i].retVal;
sum2 += i;
}
if (sum1 == sum2) {
printf("Success: Sum1 %d equals Sum2 %d", sum1, sum2);
} else {
printf("Fail: Sum1 %d does not Sum2 %d", sum1, sum2);
}
return 0;
}
//threadPoolFun function definition
void * threadPoolFun(void * arg) {
task_t * task_ptr;
int size = attr.mq_msgsize; //size = 16
char buf[size];
while (1) {
int ret_val = mq_receive(task_queue, buf, size, NULL);
if (ret_val == -1) {
perror("Error receiving message: ");
}
task_ptr = (task_t *) buf;
if (task_ptr->func == NULL) {
break;
}
task_ptr->func(task_ptr->this);
}
return NULL;
}
Any help is appreciated, many thanks already in advance!
Lawrence
The problem is that you use addresses to objects on the stack after they are no longer there. You have this:
for (int i = 0; i < NTASKS; i++) {
fooTask_t task;
task.tid = i;
fooInit(&task);
td[i] = task;
}
&task is such a pointer. This is easy to fix, since you already have a place where you store the task. Here is better code:
for (int i = 0; i < NTASKS; i++) {
fooTask_t task;
task.tid = i;
td[i] = task;
fooInit(&td[i]);
}
The second place where this happens is here:
for (int i = 0; i < NTASKS; i++) {
printf("Sending dummy task %d\n", i);
task_t dummy_task;
dummy_task.this = & dummy_task;
dummy_task.func = NULL;
mq_send(task_queue, (const char * ) & dummy_task, sizeof(task_t), 0);
}
Since the dummy tasks don't need to be individually saved anywhere, you can move out the declaration of dummy_task, so it will live during the whole execution of main:
task_t dummy_task;
dummy_task.this = & dummy_task;
dummy_task.func = NULL;
//send dummy tasks with func==NULL to terminate threads
for (int i = 0; i < NTASKS; i++) {
fprintf(stderr, "Sending dummy task %d\n", i);
mq_send(task_queue, (const char * ) & dummy_task, sizeof(task_t), 0);
}
After this, there are still synchronization problems with your algorithm, but that is a separate problem, which I leave to you.
I changed the printing from stdout to stderr, since the former is buffered, and got lost when the program crashed.
PS: I am very new to threads.
I have a problem where i need to wait for connection requests(completely arbitrary number of times) from clients, accept a connection on a socket, create a worker thread after connection. The created thread then creates a char array, works on it and needs to pass it to the parent process.
I have been able to create the threads in a while loop like
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
}
I have seen that pthread_join() can be used to pass data from thread to parent process(in unix). My question is, how can I integrate it into a loop in the main process.
I expect the following approach will result in a situation where no more than one connection can be established between client and server at a time,which is not desired.
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
pthread_join(thread_id,&my_array);
}
EDIT: I would be happy to know if what I want is impossible or if there are alternatives to pthread_join(). or its windows equivalent.
EDIT: I know that pthread_join() is for Unix and have read that WaitForMultipleObjects() is its equivalent for windows. In any case I still haven't been able to figure out a solution.
I have seen that pthread_join() can be used to pass data from thread to parent process.
That is not entirely correct. You can pass a pointer when you exit a thread, and collect that pointer using pthread_join. You have to implement all the logic yourself. The API does not know (or care) what the pointer is. Threads don't have parents and children, they are siblings.
Example for a creator and a reaper:
global
struct VarLengthArray {
size_t count;
MyElem data[1];
};
exiting thread:
// allocate the result
size_t count = ...;
VarLengthArray *retval = malloc(
sizeof(VarLengthArray) +
sizeof(MyElem) * (count > 0 ? count - 1 : 0)
);
// fill the result
retval->count = count;
for (size_t i = 0; i < retval->count; ++i) {
retval->data[i] = ...;
}
pthread_exit(retval);
collecting thread:
// collect the result
void *retval_;
if (pthread_join(thread_one_id, &retval_) != 0) {
// handle error
}
VarLengthArray *retval = retval_;
// use the result
for (size_t i = 0; i < retval->count; ++i) {
printf("retval->[%u] = %s\n", (unsigned) i, retval->data[i].string_value);
}
// deallocate the result
free(retval);
A full example using a condition variable and multiple creators:
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct Datum {
struct Datum *next;
char some_data[32];
} Datum;
typedef struct SharedData {
pthread_mutex_t mutex;
pthread_cond_t cond_empty;
unsigned seed;
Datum *head, *tail;
unsigned children_alive;
} SharedData;
static void *thread_logic(void *argv_);
int main(int argc, char **argv) {
unsigned thread_count = 2;
if (argc > 1) {
if (sscanf(argv[1], " %u ", &thread_count) != 1) {
fprintf(stderr, "Usage: %s [thread_count]\n", argv[0]);
return 1;
}
}
// initialize shared data
SharedData shared_data;
pthread_mutex_init(&shared_data.mutex, NULL);
pthread_cond_init(&shared_data.cond_empty, NULL);
shared_data.seed = time(NULL);
shared_data.head = NULL;
shared_data.tail = NULL;
shared_data.children_alive = 0;
// start threads detached, so you don't have to call pthread_join
pthread_t *child_ids = malloc(sizeof(pthread_t) * thread_count);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// start the threads
pthread_mutex_lock(&shared_data.mutex);
for (unsigned i = 0; i < thread_count; ++i) {
if (pthread_create(&child_ids[i], &attr, thread_logic, &shared_data) != 0) {
perror("pthread_create");
} else {
++shared_data.children_alive;
}
}
pthread_mutex_unlock(&shared_data.mutex);
pthread_attr_destroy(&attr);
// loop until all threads are dead
while (shared_data.children_alive > 0) {
// a condition variable: wait until there is data you can read
pthread_mutex_lock(&shared_data.mutex);
while (shared_data.head == NULL) {
pthread_cond_wait(&shared_data.cond_empty, &shared_data.mutex);
}
// collect a first datum
Datum *datum = shared_data.head;
if (datum->next != NULL) {
shared_data.head = datum->next;
} else {
shared_data.head = shared_data.tail = NULL;
}
pthread_mutex_unlock(&shared_data.mutex);
// handle the data (outside of the mutex lock)
printf("Got data: %s\n", datum->some_data);
free(datum);
}
return 0;
}
static void *thread_logic(void *shared_data_) {
SharedData *shared_data = shared_data_;
while (1) {
pthread_mutex_lock(&shared_data->mutex);
// create some data
useconds_t timeout = (
(((float) (unsigned) rand_r(&shared_data->seed)) / UINT_MAX) *
1000000
);
Datum *datum = malloc(sizeof(Datum));
datum->next = NULL;
if (timeout < 1000000 / 25) {
--shared_data->children_alive;
snprintf(datum->some_data, sizeof(datum->some_data), "I'm done\n");
} else {
snprintf(
datum->some_data, sizeof(datum->some_data),
"Sleeping for %uus\n", timeout
);
}
// append the datum
if (shared_data->head) {
shared_data->tail->next = datum;
} else {
shared_data->head = datum;
pthread_cond_signal(&shared_data->cond_empty);
}
shared_data->tail = datum;
pthread_mutex_unlock(&shared_data->mutex);
// most likely it takes some time to create the data
// do lengthly tasks outside of the mutex lock
if (timeout < 1000000 / 25) {
return NULL;
} else {
usleep(timeout);
}
}
}
I'm writing a simple program that reads urls from a text file, and checks the validity of them using multi threaded programming. I used mutexes and condition variables to synchronize my threads but my app still crashes and after some debugging sessions I decided to get some help :)
The input of the file is a text file containing urls like:
http://www.youtube.com/
http://www.facebook.com/
And the output should be the aggregated result of the curl request on each url (whether it returns OK, UNKOWN or ERROR)
This is my code:
/*
* ex3.c
*/
#define _GNU_SOURCE
#include <curl/curl.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define REQUEST_TIMEOUT_SECONDS 2L
#define URL_OK 0
#define URL_ERROR 1
#define URL_UNKNOWN 2
#define QUEUE_SIZE 32
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
typedef struct {
int ok, error, unknown;
} UrlStatus;
typedef struct {
void **array;
int size;
int capacity;
int head;
int tail;
pthread_mutex_t mutex;
pthread_cond_t cv_empty; /* get notified when the queue is not full */
pthread_cond_t cv_full; /* get notified when the queue is not empty */
} Queue;
void queue_init(Queue *queue, int capacity) {
/*
* Initializes the queue with the specified capacity.
* This function should allocate the internal array, initialize its properties
* and also initialize its mutex and condition variables.
*/
queue->array = (void**)malloc(sizeof(void*) * capacity);
if (queue->array == NULL) {
perror("unable to allocate memory");
exit(EXIT_FAILURE);
}
queue->capacity = capacity;
queue->size = 0;
queue->head = 0;
queue->tail = 0;
pthread_mutex_init(&(queue->mutex), NULL);
pthread_cond_init(&(queue->cv_empty), NULL);
pthread_cond_init(&(queue->cv_full), NULL);
}
void enqueue(Queue *queue, void *data) {
/*
* Enqueue an object to the queue.
*
* TODO:
* 1. This function should be synchronized on the queue's mutex
* 2. If the queue is full, it should wait until it is not full
* (i.e. cv_empty)
* 3. Add an element to the tail of the queue, and update the tail & size
* parameters
* 4. Signal that the queue is not empty (i.e. cv_full)
*/
pthread_mutex_lock(&(queue->mutex));
while (queue->size >= QUEUE_SIZE) {
pthread_cond_wait(&(queue->cv_empty), &(queue->mutex));
}
if(queue->size == 0) {
queue->head = 0;
}
queue->array[queue->tail] = data;
queue->size++;
queue->tail++;
pthread_cond_signal(&(queue->cv_full));
pthread_mutex_unlock(&(queue->mutex));
}
void *dequeue(Queue *queue) {
/*
* Dequeue an object from the queue.
*
* TODO:
* 1. This function should be synchronized on the queue's mutex
* 2. If the queue is empty, it should wait until it is not empty (i.e. cv_full)
* 3. Read the head element, and update the head & size parameters
* 4. Signal that the queue is not full (i.e. cv_empty)
* 5. Return the dequeued item
*/
void *data;
pthread_mutex_lock(&(queue->mutex));
while (queue->size <= 0) {
pthread_cond_wait(&(queue->cv_full), &(queue->mutex));
}
queue->head++;
data = queue->array[queue->head];
queue->size--;
pthread_cond_signal(&(queue->cv_empty));
pthread_mutex_unlock(&(queue->mutex));
return data;
}
void queue_destroy(Queue *queue) {
/*
* Free the queue memory and destroy the mutex and the condition variables.
*/
int ret;
free(queue->array);
ret = pthread_mutex_destroy(&(queue->mutex));
if (ret != 0) {
handle_error_en(ret, "unable to destroy mutex");
}
ret = pthread_cond_destroy(&(queue->cv_empty));
if (ret != 0) {
handle_error_en(ret, "unable to destroy cv_empty condition variable");
}
ret = pthread_cond_destroy(&(queue->cv_full));
if (ret != 0) {
handle_error_en(ret, "unable to destroy cv_full condition variable");
}
}
void usage() {
fprintf(stderr, "usage:\n\t./ex3 FILENAME NUMBER_OF_THREADS\n");
exit(EXIT_FAILURE);
}
int count = 0;
int check_url(const char *url) {
CURL *curl;
CURLcode res;
long response_code = 0L;
int http_status = URL_UNKNOWN;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, REQUEST_TIMEOUT_SECONDS);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* do a HEAD request */
res = curl_easy_perform(curl);
if(res == CURLE_OK) {
res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (res == CURLE_OK &&
response_code >= 200 &&
response_code < 400) {
http_status = URL_OK;
} else {
http_status = URL_ERROR;
}
}
curl_easy_cleanup(curl);
}
return http_status;
}
typedef struct {
Queue *url_queue;
Queue *result_queue;
} WorkerArguments;
void *worker(void *args) {
/*
* TODO:
* 1. Initialize a UrlStatus (allocate memory using malloc, and zero it
* using memset)
* 2. Dequeue URL from url_queue, run check_url on it, and update results
* (don't forget to free() the url)
* 3. After dequeuing a NULL value:
* Enqueue results to result_queue and return
*/
WorkerArguments *worker_args = (WorkerArguments *)args;
UrlStatus *results = NULL;
char *url;
results = malloc(sizeof(UrlStatus));
if(results == NULL) {
perror("Could not allocate memory");
exit(-1);
}
memset(results, 0, sizeof(UrlStatus));
while((url = (char *)dequeue(worker_args->url_queue)) != NULL) {
switch(check_url(url)) {
case URL_OK:
results->ok++;
break;
case URL_UNKNOWN:
results->unknown++;
break;
case URL_ERROR:
results->error++;
break;
}
/*free(url);*/
}
enqueue(worker_args->result_queue, results);
return NULL;
}
typedef struct {
const char *filename;
Queue *url_queue;
} FileReaderArguments;
void *file_reader(void *args) {
/*
* TODO:
* 1. Open filename (use fopen, check for errors)
* 2. Use getline() to read lines (i.e. URLs) from the file (use errno to check for errors)
* 3. Copy each url to the heap (use malloc and strncpy)
* 4. Enqueue the URL to url_queue
* 5. Don't forget to free the line variable, and close the file (and check for errors!)
*/
FileReaderArguments *file_reader_args = (FileReaderArguments *)args;
FILE *toplist_file;
char *line = NULL;
char *url = NULL;
size_t len = 0;
ssize_t read = 0;
toplist_file = fopen(file_reader_args->filename, "r");
if (toplist_file == NULL) {
exit(EXIT_FAILURE);
}
while ((read = getline(&line, &len, toplist_file)) != -1) {
if (read == -1) {
perror("error reading file");
}
if(read == 1) continue; /*empty line*/
url = malloc(read);
if(url == NULL) {
perror("Could not allocate memory");
}
line[read-1] = '\0'; /* null-terminate the URL */
strncpy(url, line, read);
enqueue(file_reader_args->url_queue, url);
}
fclose(toplist_file);
return NULL;
}
typedef struct {
int number_of_threads;
Queue *url_queue;
Queue *result_queue;
} CollectorArguments;
void *collector(void *args) {
/*
* TODO:
* 1. Enqueue number_of_threads NULLs to the url_queue
* 2. Dequeue and aggregate number_of_threads thread_results
* from result_queue into results (don't forget to free() thread_results)
* 3. Print aggregated results to the screen
*/
CollectorArguments *collector_args = (CollectorArguments *)args;
UrlStatus results = {0};
UrlStatus *thread_results;
int i;
for(i= 0; i < collector_args->number_of_threads; i++) {
}
for(i= 0; i < collector_args->number_of_threads; i++) {
thread_results = dequeue(collector_args->result_queue);
results.ok += thread_results->ok;
results.error += thread_results->error;
results.unknown += thread_results->unknown;
/* free(thread_results);*/
}
printf("%d OK, %d Error, %d Unknown\n",
results.ok,
results.error,
results.unknown);
return NULL;
}
void parallel_checker(const char *filename, int number_of_threads) {
/*
* TODO:
* 1. Initialize a Queue for URLs, a Queue for results (use QUEUE_SIZE)
* 2. Start number_of_threads threads running worker()
* 3. Start a thread running file_reader(), and join it
* 4. Start a thread running collector(), and join it
* 5. Join all worker threads
* 6. Destroy both queues
*/
Queue url_queue, result_queue;
WorkerArguments worker_arguments = {0};
FileReaderArguments file_reader_arguments = {0};
CollectorArguments collector_arguments = {0};
pthread_t *worker_threads;
pthread_t file_reader_thread, collector_thread;
int i;
int err;
queue_init(&url_queue, QUEUE_SIZE);
queue_init(&result_queue, QUEUE_SIZE);
worker_arguments.url_queue = &url_queue;
worker_arguments.result_queue = &result_queue;
file_reader_arguments.filename = filename;
file_reader_arguments.url_queue = &url_queue;
collector_arguments.url_queue = &url_queue;
collector_arguments.result_queue = &result_queue;
collector_arguments.number_of_threads = number_of_threads;
worker_threads = (pthread_t *) malloc(sizeof(pthread_t) * number_of_threads);
if (worker_threads == NULL) {
perror("unable to allocate memory");
return;
}
curl_global_init(CURL_GLOBAL_ALL); /* init libcurl before starting threads */
for(i=0; i<number_of_threads; i++) {
err = pthread_create(&(worker_threads[i]), NULL, &worker, &worker_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
}
err = pthread_create(&file_reader_thread, NULL, &file_reader, &file_reader_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
err = pthread_join(file_reader_thread, NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
err = pthread_create(&collector_thread, NULL, &collector, &collector_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
err = pthread_join(collector_thread, NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
for(i=0; i<number_of_threads; i++) {
err = pthread_join(worker_threads[i], NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
}
queue_destroy(&url_queue);
queue_destroy(&result_queue);
free(worker_threads);
}
int main(int argc, char **argv) {
if (argc != 3) {
usage();
} else {
parallel_checker(argv[1], atoi(argv[2]));
}
return EXIT_SUCCESS;
}
I think that I'm missing something with the synchronization mechanisms, can anyone spot where I was wrong?
Thanks a lot!!!
One multi-threading logic flaw is definitely:
There is no distinction between "worker input queue temporarily empty" and "file reader thread finished" - Your workers simply exit when there is nothing to read (even temporarily) on their input queue. So, if the file_reader thread produces queue entries slower than the workers consume them for any reason, the consumers will starve and die, leaving the producer with no consumer and, finally, a hang.
I'm having trouble debugging the following program I wrote. The idea is to have two seperate threads; one thread executes a 5 second countdown while the other waits for key input from the user. Whichever thread completes first should cancel the sibling thread and exit the program. However, the following code just hangs.
Any help would be appreciated, but I would be most grateful for an explanation as to the problem.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep()
#define NUM_THREADS 2
// The stuct to be passed as an argument to the countdown routine
typedef struct countdown_struct {
pthread_t *thread;
signed int num_secs;
} CountdownStruct;
// Struct for passing to the input wait routine
typedef struct wait_struct {
pthread_t *thread;
int *key;
} WaitStruct;
// Countdown routine; simply acts as a timer counting down
void * countdown(void *args)
{
CountdownStruct *cd_str = (CountdownStruct *)args;
signed int secs = cd_str->num_secs;
printf("Will use default setting in %d seconds...", secs);
while (secs >= 0)
{
sleep(1);
secs -= 1;
printf("Will use default setting in %d seconds...", secs);
}
// Cancel the other struct
pthread_cancel(*(cd_str->thread));
return NULL;
}
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
do {
c = getchar();
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}
int main(int argc, char **argv)
{
pthread_t wait_thread;
pthread_t countdown_thread;
pthread_attr_t attr;
int key=0;
long numMillis=5000;
int rc=0;
int status=0;
// Create the structs to be passe as paramaters to both routines
CountdownStruct *cd_str = (CountdownStruct *) malloc(sizeof(CountdownStruct));
if (cd_str == NULL)
{
printf("Couldn't create the countdown struct. Aborting...");
return -1;
}
cd_str->thread = &wait_thread;
cd_str->num_secs = 5;
WaitStruct *wait_str = (WaitStruct *) malloc(sizeof(WaitStruct));
if (wait_str == NULL)
{
printf("Couldn't create the iput wait struct. Aborting...");
return -1;
}
wait_str->thread = &countdown_thread;
wait_str->key = &key;
// Create the joinable attribute
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Create both threads
rc = pthread_create(&countdown_thread, &attr, countdown, (void *) cd_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
rc = pthread_create(&wait_thread, &attr, wait_for_input, (void *) wait_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
// Destroy the pthread_attribute
pthread_attr_destroy(&attr);
// now join on the threads and wait for main
pthread_join(wait_thread, NULL);
pthread_join(countdown_thread, NULL);
// Call pthread_exit
pthread_exit(NULL);
// Free the function structs
free(cd_str);
free(wait_str);
}
Getchar is not required to be a cancellation point. Select and pselect are. Even if you want to continue to use a countdown thread you could still provide a cancellation point in the opposing thread by use of select.
I had reasonable behavior with the following modified wait_for_input()
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
fd_set readFds;
int numFds=0;
FD_ZERO(&readFds);
do {
struct timeval timeout={.tv_sec=8,.tv_usec=0};
/* select here is primarily to serve as a cancellation
* point. Though there is a possible race condition
* still between getchar() getting called right as the
* the timeout thread calls cancel.().
* Using the timeout option on select would at least
* cover that, but not done here while testing.
*******************************************************/
FD_ZERO(&readFds);
FD_SET(STDOUT_FILENO,&readFds);
numFds=select(STDOUT_FILENO+1,&readFds,NULL,NULL,&timeout);
if(numFds==0 )
{
/* must be timeout if no FD's selected */
break;
}
if(FD_ISSET(STDOUT_FILENO,&readFds))
{
printf("Only get here if key pressed\n");
c = getchar();
}
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}
I'm trying to use nl80211.h for scanning access points for a simple WLAN manager. I can't find any example code and only documentation I can find is kerneldoc. I have been trying to study from iw and wpa_supplicant source but it's rather complex.
This is only documentation I can find:
NL80211_CMD_GET_SCAN get scan results
NL80211_CMD_TRIGGER_SCAN trigger a new scan with the given parameters
NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
probe requests at CCK rate or not.
How can I scan access points with nl80211? I think I need to use enum nl80211_commands {NL80211_CMD_GET_SCAN NL80211_CMD_TRIGGER_SCAN}. How can I use them?
I know this is an old question but I ran across it while I was trying to do the same thing. Being new to C and libnl I struggled to get a simple C program to just spit out access points. I'm posting this here for others who were also trying to write a simple program. iw was a great reference but it was challenging following the code around since it does so much more than scan for access points.
/*
* scan_access_points.c: Prints all detected access points with wlan0 using NL80211 (netlink).
*
* Only works on network interfaces whose drivers are compatible with Netlink. Test this by running `iw list`.
*
* Since only privileged users may submit NL80211_CMD_TRIGGER_SCAN, you'll have to run the compiled program as root.
*
* Build with: gcc $(pkg-config --cflags --libs libnl-genl-3.0) scan_access_points.c
*
* Raspbian prerequisites:
* sudo apt-get install libnl-genl-3-dev
*
* Resources:
* http://git.kernel.org/cgit/linux/kernel/git/jberg/iw.git/tree/scan.c
* http://stackoverflow.com/questions/21601521/how-to-use-the-libnl-library-to-trigger-nl80211-commands
* http://stackoverflow.com/questions/23760780/how-to-send-single-channel-scan-request-to-libnl-and-receive-single-
*
* Expected output (as root):
* NL80211_CMD_TRIGGER_SCAN sent 36 bytes to the kernel.
* Waiting for scan to complete...
* Got NL80211_CMD_NEW_SCAN_RESULTS.
* Scan is done.
* NL80211_CMD_GET_SCAN sent 28 bytes to the kernel.
* 47:be:34:f0:bb:be, 2457 MHz, NETGEAR16
* 6b:db:ed:85:ef:42, 2432 MHz, NETGEAR31
* d8:06:ef:a7:f9:80, 2412 MHz, ATT912
* a7:0d:af:0a:19:08, 2462 MHz, ATT185
*
* Expected output (without root):
* NL80211_CMD_TRIGGER_SCAN sent 36 bytes to the kernel.
* Waiting for scan to complete...
* error_handler() called.
* WARNING: err has a value of -1.
* ERROR: nl_recvmsgs() returned -28 (Operation not permitted).
* do_scan_trigger() failed with -28.
*
*/
#include <errno.h>
#include <netlink/errno.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <linux/nl80211.h>
struct trigger_results {
int done;
int aborted;
};
struct handler_args { // For family_handler() and nl_get_multicast_id().
const char *group;
int id;
};
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
// Callback for errors.
printf("error_handler() called.\n");
int *ret = arg;
*ret = err->error;
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg) {
// Callback for NL_CB_FINISH.
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int ack_handler(struct nl_msg *msg, void *arg) {
// Callback for NL_CB_ACK.
int *ret = arg;
*ret = 0;
return NL_STOP;
}
static int no_seq_check(struct nl_msg *msg, void *arg) {
// Callback for NL_CB_SEQ_CHECK.
return NL_OK;
}
static int family_handler(struct nl_msg *msg, void *arg) {
// Callback for NL_CB_VALID within nl_get_multicast_id(). From http://sourcecodebrowser.com/iw/0.9.14/genl_8c.html.
struct handler_args *grp = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int rem_mcgrp;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS]) return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { // This is a loop.
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), nla_len(mcgrp), NULL);
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) continue;
if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), grp->group,
nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) {
continue;
}
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return NL_SKIP;
}
int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group) {
// From http://sourcecodebrowser.com/iw/0.9.14/genl_8c.html.
struct nl_msg *msg;
struct nl_cb *cb;
int ret, ctrlid;
struct handler_args grp = { .group = group, .id = -ENOENT, };
msg = nlmsg_alloc();
if (!msg) return -ENOMEM;
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
ret = -ENOMEM;
goto out_fail_cb;
}
ctrlid = genl_ctrl_resolve(sock, "nlctrl");
genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
ret = -ENOBUFS;
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
ret = nl_send_auto_complete(sock, msg);
if (ret < 0) goto out;
ret = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp);
while (ret > 0) nl_recvmsgs(sock, cb);
if (ret == 0) ret = grp.id;
nla_put_failure:
out:
nl_cb_put(cb);
out_fail_cb:
nlmsg_free(msg);
return ret;
}
void mac_addr_n2a(char *mac_addr, unsigned char *arg) {
// From http://git.kernel.org/cgit/linux/kernel/git/jberg/iw.git/tree/util.c.
int i, l;
l = 0;
for (i = 0; i < 6; i++) {
if (i == 0) {
sprintf(mac_addr+l, "%02x", arg[i]);
l += 2;
} else {
sprintf(mac_addr+l, ":%02x", arg[i]);
l += 3;
}
}
}
void print_ssid(unsigned char *ie, int ielen) {
uint8_t len;
uint8_t *data;
int i;
while (ielen >= 2 && ielen >= ie[1]) {
if (ie[0] == 0 && ie[1] >= 0 && ie[1] <= 32) {
len = ie[1];
data = ie + 2;
for (i = 0; i < len; i++) {
if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\') printf("%c", data[i]);
else if (data[i] == ' ' && (i != 0 && i != len -1)) printf(" ");
else printf("\\x%.2x", data[i]);
}
break;
}
ielen -= ie[1] + 2;
ie += ie[1] + 2;
}
}
static int callback_trigger(struct nl_msg *msg, void *arg) {
// Called by the kernel when the scan is done or has been aborted.
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct trigger_results *results = arg;
//printf("Got something.\n");
//printf("%d\n", arg);
//nl_msg_dump(msg, stdout);
if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
printf("Got NL80211_CMD_SCAN_ABORTED.\n");
results->done = 1;
results->aborted = 1;
} else if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
printf("Got NL80211_CMD_NEW_SCAN_RESULTS.\n");
results->done = 1;
results->aborted = 0;
} // else probably an uninteresting multicast message.
return NL_SKIP;
}
static int callback_dump(struct nl_msg *msg, void *arg) {
// Called by the kernel with a dump of the successful scan's data. Called for each SSID.
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
char mac_addr[20];
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct nlattr *bss[NL80211_BSS_MAX + 1];
static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
[NL80211_BSS_TSF] = { .type = NLA_U64 },
[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
[NL80211_BSS_BSSID] = { },
[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
[NL80211_BSS_INFORMATION_ELEMENTS] = { },
[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
[NL80211_BSS_BEACON_IES] = { },
};
// Parse and error check.
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_BSS]) {
printf("bss info missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
printf("failed to parse nested attributes!\n");
return NL_SKIP;
}
if (!bss[NL80211_BSS_BSSID]) return NL_SKIP;
if (!bss[NL80211_BSS_INFORMATION_ELEMENTS]) return NL_SKIP;
// Start printing.
mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
printf("%s, ", mac_addr);
printf("%d MHz, ", nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
print_ssid(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
printf("\n");
return NL_SKIP;
}
int do_scan_trigger(struct nl_sock *socket, int if_index, int driver_id) {
// Starts the scan and waits for it to finish. Does not return until the scan is done or has been aborted.
struct trigger_results results = { .done = 0, .aborted = 0 };
struct nl_msg *msg;
struct nl_cb *cb;
struct nl_msg *ssids_to_scan;
int err;
int ret;
int mcid = nl_get_multicast_id(socket, "nl80211", "scan");
nl_socket_add_membership(socket, mcid); // Without this, callback_trigger() won't be called.
// Allocate the messages and callback handler.
msg = nlmsg_alloc();
if (!msg) {
printf("ERROR: Failed to allocate netlink message for msg.\n");
return -ENOMEM;
}
ssids_to_scan = nlmsg_alloc();
if (!ssids_to_scan) {
printf("ERROR: Failed to allocate netlink message for ssids_to_scan.\n");
nlmsg_free(msg);
return -ENOMEM;
}
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
printf("ERROR: Failed to allocate netlink callbacks.\n");
nlmsg_free(msg);
nlmsg_free(ssids_to_scan);
return -ENOMEM;
}
// Setup the messages and callback handler.
genlmsg_put(msg, 0, 0, driver_id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0); // Setup which command to run.
nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index); // Add message attribute, which interface to use.
nla_put(ssids_to_scan, 1, 0, ""); // Scan all SSIDs.
nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids_to_scan); // Add message attribute, which SSIDs to scan for.
nlmsg_free(ssids_to_scan); // Copied to `msg` above, no longer need this.
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback_trigger, &results); // Add the callback.
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); // No sequence checking for multicast messages.
// Send NL80211_CMD_TRIGGER_SCAN to start the scan. The kernel may reply with NL80211_CMD_NEW_SCAN_RESULTS on
// success or NL80211_CMD_SCAN_ABORTED if another scan was started by another process.
err = 1;
ret = nl_send_auto(socket, msg); // Send the message.
printf("NL80211_CMD_TRIGGER_SCAN sent %d bytes to the kernel.\n", ret);
printf("Waiting for scan to complete...\n");
while (err > 0) ret = nl_recvmsgs(socket, cb); // First wait for ack_handler(). This helps with basic errors.
if (err < 0) {
printf("WARNING: err has a value of %d.\n", err);
}
if (ret < 0) {
printf("ERROR: nl_recvmsgs() returned %d (%s).\n", ret, nl_geterror(-ret));
return ret;
}
while (!results.done) nl_recvmsgs(socket, cb); // Now wait until the scan is done or aborted.
if (results.aborted) {
printf("ERROR: Kernel aborted scan.\n");
return 1;
}
printf("Scan is done.\n");
// Cleanup.
nlmsg_free(msg);
nl_cb_put(cb);
nl_socket_drop_membership(socket, mcid); // No longer need this.
return 0;
}
int main() {
int if_index = if_nametoindex("wlan0"); // Use this wireless interface for scanning.
// Open socket to kernel.
struct nl_sock *socket = nl_socket_alloc(); // Allocate new netlink socket in memory.
genl_connect(socket); // Create file descriptor and bind socket.
int driver_id = genl_ctrl_resolve(socket, "nl80211"); // Find the nl80211 driver ID.
// Issue NL80211_CMD_TRIGGER_SCAN to the kernel and wait for it to finish.
int err = do_scan_trigger(socket, if_index, driver_id);
if (err != 0) {
printf("do_scan_trigger() failed with %d.\n", err);
return err;
}
// Now get info for all SSIDs detected.
struct nl_msg *msg = nlmsg_alloc(); // Allocate a message.
genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0); // Setup which command to run.
nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index); // Add message attribute, which interface to use.
nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, callback_dump, NULL); // Add the callback.
int ret = nl_send_auto(socket, msg); // Send the message.
printf("NL80211_CMD_GET_SCAN sent %d bytes to the kernel.\n", ret);
ret = nl_recvmsgs_default(socket); // Retrieve the kernel's answer. callback_dump() prints SSIDs to stdout.
nlmsg_free(msg);
if (ret < 0) {
printf("ERROR: nl_recvmsgs_default() returned %d (%s).\n", ret, nl_geterror(-ret));
return ret;
}
return 0;
}
nl80211.h only provides these enums for you to use with the real wireless library (which is libnl). You can use libnl by downloading it and including it in your c program: http://www.carisma.slowglass.com/~tgr/libnl/
Then with nl80211.h included, you can use all the enums that are defined with the commands defined in libnl.