Related
In this code, I am trying to monitor two paths at the same time. I used while(1) for this purpose. But the problem that I am facing is that whenever I run the code, it gives me the same result two times like this.
Giving result
Pathname1 "file" is modified
Pathname1 "file" is modified
Expected result
Pathname1 "file" is modified
I debugged the code. After breaking the main function and stepping over it, the next command stops at this line length = read(fd, buffer, EVENT_BUF_LEN ). Whenever I break a line after this length variable command, the program starts and after modifying the file, the program stops at this line struct inotify_event *event = ( struct inotify_event *)&buffer[i]; Although the program should not break.
I also used IN_CLOSE_WRITE instead of IN_MODIFY but no change in the result.
typedef struct{
int length, fd, wd1, wd2;
char buffer[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1, char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);
while(1){
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
int i = 0;
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
if(event->len){
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n", event->name);
break;
}
if(event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n", event->name);
break;
}
}
}
i += EVENT_SIZE + event->len;
}
}
inotify_rm_watch(inotify.fd, inotify.wd1);
inotify_rm_watch(inotify.fd, inotify.wd2);
close(inotify.fd);
exit(0);
}
Some remarks:
You should not "break" as you go out of the inside "while" loop and call read() again
The IN_MODIFY event does not fill the event->name field (i.e. event->len = 0)
The number of IN_MODIFY events depends on how you modify the files ("echo xxx >> file" = write only = 1 IN_MODIFY; "echo xxx > file" = truncate + write = 2 IN_MODIFY)
Here is a proposition for your program (with additional prints):
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <stdlib.h>
#define EVENT_BUF_LEN 4096
#define EVENT_SIZE sizeof(struct inotify_event)
typedef struct{
int length, fd, wd1, wd2;
char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1, char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
printf("wd1 = %d\n", inotify.wd1);
inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);
printf("wd2 = %d\n", inotify.wd2);
while(1){
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
int i = 0;
printf("read() = %d\n", inotify.length);
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
printf("event->len = %u\n", event->len);
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 is modified\n");
} else if (event->wd == inotify.wd2){
printf("Pathname2 is modified\n");
}
}
i += (EVENT_SIZE + event->len);
printf("i=%d\n", i);
}
}
inotify_rm_watch(inotify.fd, inotify.wd1);
inotify_rm_watch(inotify.fd, inotify.wd2);
close(inotify.fd);
exit(0);
}
int main(void)
{
getNotified("/tmp/foo", "/tmp/bar");
return 0;
} // main
Here is an example of execution:
$ gcc notif.c -o notif
$ > /tmp/foo
$ > /tmp/bar
$ ./notif
wd1 = 1
wd2 = 2
====== Upon "> /tmp/foo": 1 event (truncate operation)
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty > /tmp/foo": 2 events (write operation, one event for truncate operation and one for the write of "qwerty" at the beginning of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
If the two pathnames refer to the same inode, then inotify.wd1 == inotify.wd2. In that case, because you have
if (event->wd == inotify.wd1) {
printf("Pathname1 '%s' is modified\n", event->name);
}
if (event->wd == inotify.wd2) {
printf("Pathname2 '%s' is modified\n", event->name);
}
both bodies would be executed; but the outputs would differ (Pathname1 ... and Pathname2 ...).
You really should be monitoring the directories the files reside in, rather than the actual pathnames, so you can catch rename events also. (Many editors create a temporary file, and rename or hardlink the temporary file over the old file, so that programs see either the old or the new file, and never a mix of the two.)
Consider the following program, which does not exhibit any undue duplication of events:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = signum;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
return sigaction(signum, &act, NULL);
}
struct monitor {
/* Supplied by caller */
const char *directory;
const char *pathname;
int (*modified)(struct monitor *);
int (*completed)(struct monitor *);
/* Reserved for internal use */
int dirwatch;
};
int monitor_files(struct monitor *const list, const size_t count)
{
char *events_ptr = NULL;
size_t events_len = 65536;
size_t i;
int err;
/* Verify sane parameters */
if (count < 1) {
errno = ENOENT;
return -1;
} else
if (!list) {
errno = EINVAL;
return -1;
}
for (i = 0; i < count; i++) {
if (!list[i].directory || !list[i].directory[0]) {
errno = EINVAL;
return -1;
}
if (!list[i].pathname || !list[i].pathname[0]) {
errno = EINVAL;
return -1;
}
list[i].dirwatch = -1;
}
/* Obtain a descriptor for inotify event queue */
int queue = inotify_init1(IN_CLOEXEC);
if (queue == -1) {
/* errno set by inotify_init1() */
return -1;
}
/* Use a reasonable dynamically allocated buffer for events */
events_ptr = malloc(events_len);
if (!events_ptr) {
close(queue);
errno = ENOMEM;
return -1;
}
/* Add a watch for each directory to be watched */
for (i = 0; i < count; i++) {
list[i].dirwatch = inotify_add_watch(queue, list[i].directory, IN_CLOSE_WRITE | IN_MOVED_TO | IN_MODIFY);
if (list[i].dirwatch == -1) {
err = errno;
close(queue);
free(events_ptr);
errno = err;
return -1;
}
}
/* inotify event loop */
err = 0;
while (!done) {
ssize_t len = read(queue, events_ptr, events_len);
if (len == -1) {
/* Interrupted due to signal delivery? */
if (errno == EINTR)
continue;
/* Error */
err = errno;
break;
} else
if (len < -1) {
/* Should never occur */
err = EIO;
break;
} else
if (len == 0) {
/* No events watched anymore */
err = 0;
break;
}
char *const end = events_ptr + len;
char *ptr = events_ptr;
while (ptr < end) {
struct inotify_event *event = (struct inotify_event *)ptr;
/* Advance buffer pointer for next event */
ptr += sizeof (struct inotify_event) + event->len;
if (ptr > end) {
close(queue);
free(events_ptr);
errno = EIO;
return -1;
}
/* Call all event handlers, even duplicate ones */
for (i = 0; i < count; i++) {
if (event->wd == list[i].dirwatch && !strcmp(event->name, list[i].pathname)) {
if ((event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE)) && list[i].completed) {
err = list[i].completed(list + i);
if (err)
break;
} else
if ((event->mask & IN_MODIFY) && list[i].modified) {
err = list[i].modified(list + i);
if (err)
break;
}
}
}
if (err)
break;
}
if (err)
break;
}
close(queue);
free(events_ptr);
errno = 0;
return err;
}
static int report_modified(struct monitor *m)
{
printf("%s/%s: Modified\n", m->directory, m->pathname);
fflush(stdout);
return 0;
}
static int report_completed(struct monitor *m)
{
printf("%s/%s: Completed\n", m->directory, m->pathname);
fflush(stdout);
return 0;
}
int main(void)
{
struct monitor watch[2] = {
{ .directory = ".",
.pathname = "file1",
.modified = report_modified,
.completed = report_completed },
{ .directory = ".",
.pathname = "file2",
.modified = report_modified,
.completed = report_completed }
};
int err;
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGTERM) == -1) {
fprintf(stderr, "Cannot set signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "To stop this program, press Ctrl+C, or send\n");
fprintf(stderr, "INT, HUP, or TERM signal (to process %ld).\n", (long)getpid());
fflush(stderr);
err = monitor_files(watch, 2);
if (err == -1) {
fprintf(stderr, "Error monitoring files: %s.\n", strerror(errno));
return EXIT_FAILURE;
} else
if (err) {
fprintf(stderr, "Monitoring files failed [%d].\n", err);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
If you compile it using e.g. gcc -Wall -Wextra -O2 example.c -o example and run it via ./example, it will report IN_MODIFY events (as "Modified") and IN_CLOSE_WRITE and IN_MOVED_TO events (as "Completed") for file1 and file2 in the same directory (current working directory). To exit the program, press Ctrl+C.
(Note that we should probably add third event type, "Deleted", corresponding to IN_DELETE and IN_MOVED_FROM events.)
If you open file1 or file2 in say a text editor, saving the file generates one or more Modified (IN_MODIFY) events, and exactly one Completed (IN_CLOSE_WRITE or IN_MOVED_TO) event. This is because each open()/truncate()/ftruncate() syscall that causes the file to be truncated, generates one IN_MODIFY event for that file; as does every underlying write() syscall that modifies the file contents. Thus, it is natural to receive multiple IN_MODIFY events when some process modifies the file.
If you have multiple struct monitor entries in the list with the same effective directory and the same pathname, the example program will provide multiple events for them. If you wish to avoid this, just make sure that whenever .pathname matches, the two get differing .dirwatches.
rewriting the while() loop into a for() loop, and flattening out the if()s"
while(1){
int i ;
struct inotify_event *event ;
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
for(i=0; i < inotify.length; i += EVENT_SIZE + event->len ) {
event = (struct inotify_event *)&inotify.buffer[i];
if (!event->len) continue;
if (!(event->mask & IN_MODIFY)) continue;
if (event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n", event->name);
continue;
}
if (event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n", event->name);
continue;
}
}
}
I am trying to set up over the air updates(OTA) updates using the example within the ESP-IDF framework library in PlaformIO. The ota simple_example main.c file imports a .h file called esp_http_client.h. esp_http_client imports another .h file called http_parser.h which is where the function http_parser_init is defined. I am building this project in PlaformIO in VScode so I checked the c_cpp_properties.json and both the http_parser.c and http_parser.h directories were included. I noticed that sometimes there are bugs with the c_cpp_properties file so I also included the the http_parser.h file directory in the platform.ini file.
I also run into the error:
undefined reference to http_parser_execute
undefined reference to http_parser_url_init
undefined reference to http_parser_parse_url
which are all functions found in the http_parser.h file
Here is the code for the main.c file that I'm using
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include <stdio.h>
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "main.h"
#define WIFI_SSID SSID
#define WIFI_PASS PASSWORD
#define SERVER_URL FIRMWARE_UPG_URL
#define BUFFSIZE 1024
#define HASH_LEN 32 /* SHA-256 digest length */
static const char *TAG = "native_ota_example";
/*an ota data write buffer ready to write to the flash*/
static char ota_write_data[BUFFSIZE + 1] = { 0 };
// uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
// uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = SSID,
.password = PASSWORD,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void http_cleanup(esp_http_client_handle_t client)
{
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
static void __attribute__((noreturn)) task_fatal_error()
{
ESP_LOGE(TAG, "Exiting task due to fatal error...");
(void)vTaskDelete(NULL);
while (1) {
;
}
}
void print_sha256 (const uint8_t *image_hash, const char *label)
{
char hash_print[HASH_LEN * 2 + 1];
hash_print[HASH_LEN * 2] = 0;
for (int i = 0; i < HASH_LEN; ++i) {
sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
}
ESP_LOGI(TAG, "%s: %s", label, hash_print);
}
static void ota_example_task(void *pvParameter)
{
esp_err_t err;
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
esp_ota_handle_t update_handle = 0 ;
const esp_partition_t *update_partition = NULL;
ESP_LOGI(TAG, "Starting OTA example...");
const esp_partition_t *configured = esp_ota_get_boot_partition();
const esp_partition_t *running = esp_ota_get_running_partition();
if (configured != running) {
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
configured->address, running->address);
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
}
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
running->type, running->subtype, running->address);
/* Wait for the callback to set the CONNECTED_BIT in the
event group.
*/
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");
esp_http_client_config_t config = {
.url = SERVER_URL,
// .cert_pem = (char *)server_cert_pem_start,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
task_fatal_error();
}
err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: 1");
esp_http_client_cleanup(client);
task_fatal_error();
}
esp_http_client_fetch_headers(client);
update_partition = esp_ota_get_next_update_partition(NULL);
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
update_partition->subtype, update_partition->address);
assert(update_partition != NULL);
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed 3");
http_cleanup(client);
task_fatal_error();
}
ESP_LOGI(TAG, "esp_ota_begin succeeded");
int binary_file_length = 0;
/*deal with all receive packet*/
while (1) {
int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
if (data_read < 0) {
ESP_LOGE(TAG, "Error: SSL data read error");
http_cleanup(client);
task_fatal_error();
} else if (data_read > 0) {
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
if (err != ESP_OK) {
http_cleanup(client);
task_fatal_error();
}
binary_file_length += data_read;
ESP_LOGD(TAG, "Written image length %d", binary_file_length);
} else if (data_read == 0) {
ESP_LOGI(TAG, "Connection closed,all data received");
break;
}
}
ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length);
if (esp_ota_end(update_handle) != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed!");
http_cleanup(client);
task_fatal_error();
}
if (esp_ota_get_running_partition() == update_partition) {
ESP_LOGI(TAG, "The current running firmware is same as the firmware just downloaded");
int i = 0;
ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
while(1) {
ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed 2!");
http_cleanup(client);
task_fatal_error();
}
ESP_LOGI(TAG, "Prepare to restart system!");
esp_restart();
return ;
}
void app_main()
{
// Initialize NVS.
nvs_flash_init();
// OTA app partition table has a smaller NVS partition size than the non-OTA
// partition table. This size mismatch may cause NVS initialization to fail.
// If this happens, we erase NVS partition and initialize NVS again.
ESP_ERROR_CHECK(nvs_flash_erase());
nvs_flash_init();
initialise_wifi();
xTaskCreate(&ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
}
The http_parser.h: file
The esp_http_client: file
I don't understand where I'm supposed to define these functions when they are defined in the esp_parser.h file or what I'm supposed to do to not get this error.
I had the same problem.
I solved it by compiling the program without PlatformIO.
https://docs.espressif.com/projects/esp-idf/en/latest/get-started/
Here are the guide to install the toolchain.
Regards
I want make a non-blocking OpenSSL connection
On this connection - if no data available for read, then entire program execution flow make stop on SSL_read(). I want so that if no data available for read it give me the returns values like WANT_READ and i know no more data available.
char *sslRead (connection *c)
{
const int readSize = 1024;
char *rc = NULL;
int r;
int received, count = 0;
int ReallocSize = 0;
char buffer[1024];
if (c)
{
while (1)
{
if (!rc)
{
rc = malloc (readSize + 1);
if (rc == NULL)
printf("the major error have happen. leave program\n");
}
else
{
ReallocSize = (count + 1) * (readSize + 1);
rc = realloc (rc, ReallocSize);
}
// if i have no data available for read after reading data,
// this call will not return anything and wait for more data
// i want change this non blocking connections
received = SSL_read (c->sslHandle, buffer, readSize);
buffer[received] = '\0';
if (received <= 0)
{
printf(" received equal to or less than 0\n");
switch (SSL_get_error(c->sslHandle, r))
{
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
default:
printf("error happens %i\n", r);
}
break;
}
count++;
}
}
return rc;
}
here is how i make connection
connection *sslConnect (void)
{
connection *c;
c = malloc (sizeof (connection));
c->sslHandle = NULL;
c->sslContext = NULL;
c->socket = tcpConnect ();
if (c->socket)
{
// Register the error strings for libcrypto & libssl
SSL_load_error_strings ();
// Register the available ciphers and digests
SSL_library_init ();
// New context saying we are a client, and using SSL 2 or 3
c->sslContext = SSL_CTX_new (SSLv23_client_method ());
if (c->sslContext == NULL)
ERR_print_errors_fp (stderr);
// Create an SSL struct for the connection
c->sslHandle = SSL_new (c->sslContext);
if (c->sslHandle == NULL)
ERR_print_errors_fp (stderr);
// Connect the SSL struct to our connection
if (!SSL_set_fd (c->sslHandle, c->socket))
ERR_print_errors_fp (stderr);
// Initiate SSL handshake
if (SSL_connect (c->sslHandle) != 1)
ERR_print_errors_fp (stderr);
}
else
{
perror ("Connect failed");
}
return c;
}
thanks you very much.
Creating a non-blocking socket is a pre-requisite to a non-blocking connect...
The following steps summarize: (see complete description in site linked below)
1) Call the fcntl() API to retrieve the socket descriptor's current flag settings into a local variable.
2) In that local variable, set the O_NONBLOCK (non-blocking) flag on. (being careful not to tamper with the other flags)
3) Call the fcntl() API to set the flags for the descriptor to the value in our local variable.
( read more on non-blocking sockets techniques here )
Assuming an existing socket, the following implements the steps outlined above:
BOOL SetSocketBlockingEnabled(SOCKET fd, BOOL blocking)
{
if (fd < 0) return FALSE;
#ifdef WIN32
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? TRUE : FALSE;
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return false;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? TRUE : FALSE;
#endif
}
Once you have a non-blocking socket, then see this post explaining how to do a non-blocking connect
I'm (synchronously) reading serial input in Windows using ReadFile(), but instead of waiting for the serial port to have input then returning that as I thought it should, ReadFile() instead returns immediately with a value of FALSE, and a GetLastError() of 0. (Yes, I'm certain I have the right error code and am not making syscalls in between).
The ReadFile() documentation says that when the function "is completing asynchronously, the return value is zero (FALSE)." How is it that a synchronous read can be completing asychronously? Why would this be an error? It's worth noting that the data read is garbage data, as one might expect.
More generally, how can I force ReadFile() to behave like a simple synchronous read of a serial port, or at least behave something like the UNIX read()?
Edit: Here is some source code:
HANDLE my_connect(char *port_name)
{
DCB dcb;
COMMTIMEOUTS timeouts;
HANDLE hdl = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
GetCommState(port_name, &dcb);
dcb.BaudRate = 115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if(SetCommState(hdl, &dcb) == 0)
{
fprintf(stderr, "SetCommState failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
/* TODO: Set a variable timeout. */
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 5000; /* wait 5s for input */
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 5000;
if(SetCommTimeouts(hdl, &timeouts) == 0)
{
fprintf(stderr, "SetCommTimeouts failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
return hdl;
}
int my_disconnect(HANDLE hdl)
{
return CloseHandle(hdl);
}
int my_send(HANDLE hdl, char *cmd)
{
DWORD nb = 0;
if(WriteFile(hdl, cmd, strlen(cmd), &nb, NULL) == 0)
{
fprintf(stderr, "WriteFile failed with error code %d.\n",
GetLastError());
return -1;
}
return (int) nb;
}
int my_receive(HANDLE hdl, char *dst, int dstlen)
{
int i;
DWORD r;
BOOL err;
char c = '\0';
for (i = 0; i < dstlen; err = ReadFile(hdl, &c, 1, &r, NULL))
{
if (err == 0)
{
fprintf(stderr, "ReadFile failed with error code %d.\n",
GetLastError());
return -1;
}
if (r > 0)
{
dst[i++] = c;
if (c == '\n') break;
}
}
if (i == dstlen)
{
fprintf(stderr, "Error: read destination buffer not large enough.\
Recommended size: 256B. Your size: %dB.\n", dstlen);
return -1;
}
else
{
dst[i] = '\0'; /* null-terminate the string. */
}
return i;
}
And my test code:
HANDLE hdl = my_connect("COM4");
char *cmd = "/home\n"; /* basic command */
char reply[256];
my_send(hdl, cmd);
my_receive(hdl, reply, 256);
puts(reply);
It's not completing asynchronously. If it were, GetLastError would return ERROR_IO_PENDING.
To do synchronous I/O, open the file without FILE_FLAG_OVERLAPPED.
It should not be possible for ReadFile to fail without a valid GetLastError code. ReadFile only returns false when the driver sets a non-success status code.
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.