Why is the pointer changing for no reason? - c

I am working on Assignment 2 of ops-class.
The following function bootstraps a file handler array for the process that is being created (eg. user processes for test programs provided here).
int _fh_bootstrap(struct fharray *fhs){
/* Initialize the file handle array of this process */
fharray_init(fhs);
/* String variables initialized for passage to vfs_open */
char* console_inp = kstrdup(CONSOLE); // CONSOLE = "con:"
char* console_out = kstrdup(console_inp);
char* console_err = kstrdup(console_inp);
/* Return variable */
int ret = 0;
/* Initialize the console files STDIN, STDOUT and STDERR */
struct vnode *stdin;
ret = vfs_open(console_inp,O_RDONLY,0,&stdin);
if(ret != 0){
return ret;
}
kfree(console_inp);
struct fh *stdinfh = kmalloc(sizeof(struct fh));
ret = _fh_create(O_RDONLY,stdin,stdinfh);
if(ret != 0){
return ret;
}
stdinfh->fd = STDIN_FILENO;
fharray_add(fhs,stdinfh,NULL);
struct vnode *stdout;
ret = vfs_open(console_out,O_WRONLY,0,&stdout);
if(ret != 0){
return ret;
}
kfree(console_out);
struct fh *stdoutfh = kmalloc(sizeof(struct fh));
ret = _fh_create(O_WRONLY,stdout,stdoutfh);
if(ret != 0){
return ret;
}
stdoutfh->fd = STDOUT_FILENO;
fharray_add(fhs,stdoutfh,NULL);
struct vnode *stderr;
ret = vfs_open(console_err,O_WRONLY,0,&stderr);
if(ret != 0){
return ret;
}
kfree(console_err);
struct fh *stderrfh = kmalloc(sizeof(struct fh));
ret = _fh_create(O_WRONLY,stderr,stderrfh);
if(ret != 0){
return ret;
}
stderrfh->fd = STDERR_FILENO;
fharray_add(fhs,stderrfh,NULL);
fharray_setsize(fhs,MAX_FD);
return 0;
/* Initialization of stdin, out and err filehandlers complete */
}
If I use os161-gdb to step through this function, I notice the following:
//*stdinfh after the call to _fh_create
{fd = 0, flag = 0, fh_seek = 0, fh_vnode = 0x80044ddc}
//**stdinfh->fh_vnode
{vn_refcount = 2, vn_countlock = {splk_lock = 0, splk_holder = 0x0}, vn_fs = 0x0,
vn_data = 0x8004ab60, vn_ops = 0x8003e690 <dev_vnode_ops>}
This is the strange part. After stepping through the second call to kmalloc (to init stdoutfh), the stdinfh->fh_vnode pointer changes value!
//**stdinfh->fh_vnode
(struct vnode *) 0x1
And even stranger, after proceeding to the following line
fharray_add(fhs,stdoutfh,NULL);
The value of *stdoutfh->fh_vnode and *stdinfh->fh_vnode IS THE SAME
1 possible explanation: Does the OS not have enough heap memory. I find it unlikely and even after assuming this, I can't exactly explain what is happening here.
Some extra code
_fh_create
struct fh definition
static int _fh_create(int flag, struct vnode *file, struct fh *handle){
KASSERT(file != NULL);
/* W , R , RW */
if (
((flag & O_RDONLY) && (flag & O_WRONLY)) ||
((flag & O_RDWR) && ((flag & O_RDONLY) || (flag & O_WRONLY)))
) {
handle = NULL;
return 1;
}
handle->flag = flag;
handle->fh_seek = 0;
handle->fh_vnode = &file;
return 0;
}
struct fh {
uint32_t fd; // file descriptor
int flag; // File handler mode
off_t fh_seek; // seek position in file
struct vnode **fh_vnode; // File object of the file
}
Definition of struct vnode can be found here.
Please let me know if you need more info and thanks for the help!

The code handle->fh_vnode is setting a pointer to automatic variable ("on the stack"), function parameters are automatic variables. After the function returns this will be a dangling pointer.
To fix this you will need to re-design your code a bit, e.g. perhaps the struct fh should just store file and not a pointer to file.

Related

Why isn't the Kernel receveing my generic netlink messages?

I'm trying to send nested attributes from user space to kernel using generic netlink, the function nl_send_auto() returns 52 (which was supposed to be the numbers of bytes sent to kernel) but the kernel isn't receiving the messages. Is there some problem with my approach? Here is the code that I wrote on user space:
int err = -1;
struct nl_msg *msg;
struct nlattr *attr;
struct nl_sock *sock;
int family;
int send = 0;
if ((sock = nl_socket_alloc()) == NULL)
return err;
if ((err = genl_connect(sock)))
return err;
if ((family = genl_ctrl_resolve(sock, FAMILY)) < 0)
return family;
if ((msg = nlmsg_alloc()) == NULL)
return err;
if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, FAMILY, 0,
NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
return err;
if (!(attr = nla_nest_start(msg, KLUA_NL_STATE))){
nla_nest_cancel(msg, attr);
return err;
}
if ((ret = nla_put_string(msg, STATE_NAME, cmd->name)) ||
(ret = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
(ret = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
)
return err;
nla_nest_end(msg, attr);
if ((send = nl_send_auto(ctrl->sock, msg)) < 0)
return send;
printf("All done sended %d bytes\n", send);
nlmsg_free(msg);
This code prints 52, which is the bytes sent to kernel;
The FAMILY macro is defined as (both in kernel and user space):
#define FAMILY "family"
My netlink attributes are (both for kernel and user space):
enum {
KLUA_NL_STATE,
STATE_NAME,
MAX_ALLOC,
CURR_ALLOC,
ATTR_COUNT,
#define ATTR_MAX (ATTR_COUNT - 1)
};
My enum for operation is:
enum {
CREATE_STATE = 16,
};
And my kernel code is:
struct nla_policy lunatik_policy[ATTR_MAX] = {
[KLUA_NL_STATE] = { .type = NLA_NESTED },
};
static int klua_create_state(struct sk_buff *buff, struct genl_info *info);
static const struct genl_ops l_ops[] = {
{
.cmd = CREATE_STATE,
.doit = klua_create_state,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0)
/*Before kernel 5.2.0, each operation has its own policy*/
.policy = lunatik_policy
#endif
},
};
#define KLUA_NL_STATE_ATTRS_COUNT 3
struct genl_family lunatik_family = {
.name = FAMILY,
.version = 1,
.maxattr = ATTR_MAX,
.netnsok = true,
.policy = lunatik_policy,
.module = THIS_MODULE,
.ops = l_ops,
.n_ops = ARRAY_SIZE(l_ops),
};
static int klua_create_state(struct sk_buff *buff, struct genl_info *info)
{
pr_info("I received the message\n");
return 0;
}
This code doesn't print anything on dmesg, and I would like to know why.
You actual problems
During Linux 5.2 refactors, the semantics of the NLA_F_NESTED flag changed somewhat. It appears you now need to always include it when you call nla_nest_start():
if (!(attr = nla_nest_start(msg, KLUA_NL_STATE))){
...
}
Should be
if (!(attr = nla_nest_start(msg, NLA_F_NESTED | KLUA_NL_STATE))){
...
}
Yes, I'm well aware the libnl library should obviously do this for you, and will perhaps do so in the future, but unfortunately this is where we are now.
Also:
enum {
KLUA_NL_STATE,
...
};
Attribute zero is always reserved. You need to change that into
enum {
KLUA_NL_STATE = 1,
...
};
Just FYI: operation zero is also reserved, so it's fortunate that you chose 16. But do keep it in mind in the future.
Syntactic issues
These are probably just copy-paste errors, but I'm including them anyway for the benefit on other people landing in this page looking for examples.
if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, FAMILY, 0,
NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
return err;
Should be
if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0,
NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
return err;
Also:
if ((ret = nla_put_string(msg, STATE_NAME, cmd->name)) ||
(ret = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
(ret = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
)
return err;
Should be
if ((err = nla_put_string(msg, STATE_NAME, cmd->name)) ||
(err = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
(err = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
)
return err;
Also:
if ((send = nl_send_auto(ctrl->sock, msg)) < 0)
return send;
Should be
if ((send = nl_send_auto(sock, msg)) < 0)
return send;

Can the Pagemap folder of processes in the Linux kernel be read(64bit per read) a finite number of times?

I'm trying to keep track of the number of writes per physical page in the file "proc/PID/pagemap".But the file is binary, and the size shown in the file properties is 0, and the following function reads 0 as well.
struct stat buf;
int iRet = fstat(fd, &buf);
if(iRet == -1)
{
perror("fstat error");
exit(-1);
}
printf("the size of file is : %ld\n", buf.st_size);
I write a monitor program to read data from a process's "pagemap" 64bit one time and record the 55-bit(soft dirty bit)to check if one page is written.Of course before doing this I cleared all soft dirty bit in a process's pagemap.This method is provided by linux kernel and my question during coding is that when I use file descriptor(also tried fstream pointer) to get the data from pagemap.My reading of pagemap ends only when the process I'm monitoring is finished, as if the file were infinite.I know the process's logical address mangement is dynamic but I want to know how could I count the write number properly.Should I read a part of this infinite file within a fixed time intervals?And how many items should I read? T _ T.
You need something like the following:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct pagemap_region {
struct pagemap_region *next;
uintptr_t addr; /* First address within region */
uintptr_t ends; /* First address after region */
size_t pages; /* Number of pages in this region */
uint64_t page[]; /* 64-bit pagemap flags per page */
};
static void free_pagemaps(struct pagemap_region *list)
{
while (list) {
struct pagemap_region *curr = list;
list = curr->next;
curr->addr = 0;
curr->ends = 0;
curr->pages = 0;
free(curr);
}
}
struct pagemap_region *get_pagemaps(const pid_t pid)
{
struct pagemap_region *list = NULL;
size_t page;
char *line_ptr = NULL;
size_t line_max = 256;
ssize_t line_len;
FILE *maps;
int n, fd;
page = sysconf(_SC_PAGESIZE);
/* We reuse this for the input line buffer. */
line_ptr = malloc(line_max);
if (!line_ptr) {
errno = ENOMEM;
return NULL;
}
/* First, fill it with the path to the map pseudo-file. */
if (pid > 0)
n = snprintf(line_ptr, line_max, "/proc/%d/maps", (int)pid);
else
n = snprintf(line_ptr, line_max, "/proc/self/maps");
if (n < 0 || (size_t)n + 1 >= line_max) {
free(line_ptr);
errno = EINVAL;
return NULL;
}
/* Read the maps pseudo-file. */
maps = fopen(line_ptr, "re"); /* Read-only, close-on-exec */
if (!maps) {
free(line_ptr);
errno = ESRCH;
return NULL;
}
while (1) {
struct pagemap_region *curr;
unsigned long addr, ends;
size_t pages;
char *ptr, *end;
line_len = getline(&line_ptr, &line_max, maps);
if (line_len < 0)
break;
/* Start address of the region. */
end = ptr = line_ptr;
errno = 0;
addr = strtoul(ptr, &end, 16);
if (errno || end == ptr || *end != '-')
break;
/* End address of the region. */
ptr = ++end;
errno = 0;
ends = strtoul(ptr, &end, 16);
if (errno || end == ptr || *end != ' ')
break;
/* Number of pages in the region. */
pages = (ends - addr) / page;
if (addr + page * pages != ends || (addr % page) != 0)
break;
/* Allocate new region map. */
curr = malloc(sizeof (struct pagemap_region) + pages * sizeof curr->page[0]);
if (!curr)
break;
curr->addr = addr;
curr->ends = ends;
curr->pages = pages;
/* Prepend to the region list. */
curr->next = list;
list = curr;
}
/* Any issues when reading the maps pseudo-file? */
if (!feof(maps) || ferror(maps)) {
fclose(maps);
free(line_ptr);
free_pagemaps(list);
errno = EIO;
return NULL;
} else
if (fclose(maps)) {
free(line_ptr);
free_pagemaps(list);
errno = EIO;
return NULL;
}
/* Reuse the line buffer for the pagemap pseudo-file path */
if (pid > 0)
n = snprintf(line_ptr, line_max, "/proc/%d/pagemap", (int)pid);
else
n = snprintf(line_ptr, line_max, "/proc/self/pagemap");
if (n < 0 || (size_t)n + 1 >= line_max) {
free(line_ptr);
free_pagemaps(list);
errno = ENOMEM;
return NULL;
}
do {
fd = open(line_ptr, O_RDONLY | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
n = errno;
free(line_ptr);
free_pagemaps(list);
errno = n;
return NULL;
}
/* Path no longer needed. */
free(line_ptr);
line_ptr = NULL;
line_max = 0;
/* Read each pagemap section. */
for (struct pagemap_region *curr = list; curr != NULL; curr = curr->next) {
off_t offset = (size_t)(curr->addr / page) * (sizeof curr->page[0]);
unsigned char *ptr = (unsigned char *)&(curr->page[0]);
size_t need = curr->pages * sizeof curr->page[0];
ssize_t bytes;
while (need > 0) {
bytes = pread(fd, ptr, need, offset);
if (bytes >= need)
break;
else
if (bytes > 0) {
ptr += bytes;
offset += bytes;
need -= bytes;
} else
if (bytes == 0) {
/* Assume this is a region we can't access, like [VSYSCALL]; clear the rest of the bits. */
memset(ptr, 0, need);
break;
} else
if (bytes != -1 || errno != EINTR) {
close(fd);
free_pagemaps(list);
errno = EIO;
return NULL;
}
}
}
if (close(fd) == -1) {
free_pagemaps(list);
errno = EIO;
return NULL;
}
return list;
}
int main(int argc, char *argv[])
{
struct pagemap_region *list, *curr;
long pid;
char *end;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv && argv[1]) ? argv[1] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s PID\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "This program prints the a map of the pages of process PID;\n");
fprintf(stderr, "R for pages in RAM, S for pages in swap space, and . for others.\n");
fprintf(stderr, "You can use -1 for the PID of this process itself.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
end = argv[1];
errno = 0;
pid = strtol(argv[1], &end, 10);
if (errno || end == argv[1] || *end) {
fprintf(stderr, "%s: Invalid PID.\n", argv[1]);
return EXIT_FAILURE;
}
if (pid != -1 && (pid < 1 || (long)(pid_t)pid != pid)) {
fprintf(stderr, "%s: Not a valid PID.\n", argv[1]);
return EXIT_FAILURE;
}
list = get_pagemaps(pid);
if (!list) {
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}
for (curr = list; curr != NULL; curr = curr->next) {
printf("Region %p - %p: %zu pages\n", (void *)(curr->addr), (void *)(curr->ends), curr->pages);
for (uint64_t *map = curr->page; map < curr->page + curr->pages; map++) {
if ((*map >> 63) & 1)
putchar('R');
else
if ((*map >> 62) & 1)
putchar('S');
else
putchar('.');
}
putchar('\n');
}
return EXIT_SUCCESS;
}
We read /proc/PID/maps line by line, and construct a struct pagemap_region for each; this contains the start address, the end address, and the number of pages in the region. (I didn't bother to support huge pages, though; if you do, consider parsing /proc/PID/smaps instead. If a line begins with a 0-9 or lowercase a-f, it specifies an region; otherwise the line begins with a capital letter A-Z and specifies a property of that region.)
Each struct pagemap_region also contains room for the 64-bit pagemap value per page. After the regions have been found/chosen – this one tries all –, the /proc/PID/pagemap file is opened, and the corresponding data read from the proper location using pread(), which works like read(), but also takes the file offset as an extra parameter.
Not all regions are accessible. I do believe [VSYSCALL] is one of those, but being a kernel-userspace interface, its pagemap bits are uninteresting anyway. Instead of removing such regions from the list, the above just clears the bits to zero.
This is not intended as a "do it exactly like this, just copy and paste this" answer, but as a suggestion of how to start going about this, perhaps exploring a bit, comparing the results or behaviour to your particular needs; a sort of a rough outline for an initial suggestion only.
Also, as I wrote it in a single sitting, it's likely got nasty bugs in it. (If I knew where or for sure, I'd fix them; it's just that bugs happen.)

C: Remaining bytes on AF_UNIX socket

I got some problems with AF_UNIX socket communication because after writing a data buffer there seem to remain some hanging bytes to read that I do not know where they come from.
I am writing a multithreaded server program in C that communicates with clients through AF_UNIX sockets, it must implement a simple chatroom. Among other things, the server must implement file transfers between clients and servers and i encountered problems when i try to send a quite large file (269K) from Server to client. (With smaller files i do not have any problems)
For file transfer i use mmap() function which return a pointer to the map of the file I want to send, then i use write() for write that data on socket linked with the client that must recieve the file.
After write() call i check the returned value to be equal than the file size. (always verified)
The client, after receiving the file, check the size of read data (always verified) and start waiting for other messages so it call a blocking read(). This is the point where I found the error because the client reads something that should not be there, as if there was something left to read on the socket.
I've been debugging this part (both server and client) for two days and I have not yet been able to understand the origin of the problem.
I am sure that no other thread write on the same socket at the same time
Does any of you have an idea of what the cause of this error is?
I try to post some useful code thinking at a normal operation sequence:
First of all message structure:
struct message_hdr
{
op_t op;
char sender[MAX_NAME_LENGTH+1];
};
struct message_data_hdr{
char receiver[MAX_NAME_LENGTH+1];
unsigned int len;
};
struct message_data
{
message_data_hdr_t hdr;
char *buf;
};
struct message
{
message_hdr_t hdr;
message_data_t data;
};
A server->client file transfer starts with server that send a message_hdr_t to a client which is waiting on a read() (the client expects to receive only a message_hdr_t).
int sendHeader(long fd, message_hdr_t* hdr)
{
if(hdr == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = hdr;
iov.iov_len = sizeof(message_hdr_t);
test = writev(fd, &iov, 1);
return test;
}
The client understands from the operation code (message.hdr.op) that it is a file type message and it begins to wait for file,
So server send it:
int sendData(long fd, message_data_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = &(msg->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = writev(fd, &(iov), 1);
if(test == -1){return -1;}
if (msg->hdr.len != 0)
{
test = write(fd, msg->buf, msg->hdr.len);
if(test <= 0)
return -1;
}
return test;
}
And client read it:
int readData(long fd, message_data_t *data)
{
if(data == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = &(data->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = readv(fd, &iov, 1);
if(test <= 0){return -1;}
if(data->hdr.len != 0)
{
data->buf = malloc(data->hdr.len);
if(data->buf == NULL){return -1;}
test = read(fd, data->buf, data->hdr.len);
if((unsigned int)test != data->hdr.len)
return -1;
}
return test;
}
At this point the client recived file, and it restart waiting for new messages:
int readMsg(long fd, message_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
test = readHeader(fd, &(msg->hdr));
if(test == -1 || test == 0){return -1;}
test += readData(fd, &(msg->data));
return test;
}
This is the point where the client should simply wait because there is no income messages, insted in this case it read something that I do not know where it comes from.
When i try to print this unwanted message with GDB it prints:
{hdr = {op = 512,
sender = "\000\000\020G\032\324\t\000\000\n\000\000\000\000\030\021B\bC\n\000\000\v\000\000\000\000\021D\v\222\000"},
data = {hdr = {receiver = "\000\000\000\000\021E\022C\n\000\000\b\v\000\000\000\000\021F\020I\n\000\000\020\000\006\b\002\n\000\000\006",
len = 131072},
buf = 0x7ffff7f2f010 ""}`
Of course this is meaningless.
I hope that this description will be useful
Thank you all in advance.
Ok, I solved my issue.
As written in the comment, this problem was due to the lack of a check on partial writing.
Now the function readData() looks like this:
int readData(long fd, message_data_t *data)
{
if(data == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
char* ph;
unsigned int rd = 0;
struct iovec iov;
iov.iov_base = &(data->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = readv(fd, &iov, 1);
if(test <= 0){return -1;}
if(data->hdr.len != 0)
{
data->buf = malloc(data->hdr.len);
if(data->buf == NULL){return -1;}
ph = data->buf;
while (rd < data->hdr.len)
{
test = read(fd, ph, data->hdr.len - rd);
if(test == -1)
return -1;
else if(test == 0)
{
errno = ENOENT;
return -1;
}
rd += test;
ph += test;
}
}
return rd;
}
and sendData():
int sendData(long fd, message_data_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
char* ph;
unsigned int wr = 0;
struct iovec iov;
iov.iov_base = &(msg->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = writev(fd, &(iov), 1);
if(test == -1){return -1;}
if(msg->hdr.len != 0)
{
ph = msg->buf;
while (wr < msg->hdr.len)
{
test = write(fd, ph, msg->hdr.len - wr);
if(test == -1)
return -1;
else if(test == 0)
{
errno = ENOENT;
return -1;
}
wr += test;
ph += test;
}
}
return test;
}
In this way I no longer found the error.
Thanks for the help!

event_new() function fails on hpux itanium

I'm trying to debug a code that is using a libevent library. In that library, there is a function event_new that is suppose to create an event_cb. Somehow after I dispatch the event base, the event_cb cannot be called or accessed. This problem only happens on hpux itanium. This code works on hpux pa-risc, Redhat, AIX, and Solaris. Is there any certain thing that need to be set?
This is part of the code
int ttypread (int fd, Header *h, char **buf)
{
int c,k;
struct user_data user_data;
struct bufferevent *in_buffer;
struct event_config *evconfig;
log_debug("inside ttypread");
in_buffer = NULL;
user_data.fd = fd;
user_data.h = h;
user_data.buf = buf;
log_debug("from user_data, fd = %d",user_data.fd); //the log_debug is a debugging function for me to check the value sent by the system. I use it to compare between each platform
log_debug("from user_data, buf = %s",user_data.buf);
log_debug("from user_data, h.len = %d",user_data.h->len);
log_debug("from user_data, h.type = %d",user_data.h->type);
evconfig = event_config_new();
if (evconfig == NULL) {
log_error("event_config_new failed");
return -1;
}
if (event_config_require_features(evconfig, EV_FEATURE_FDS)!=0) {
log_error("event_config_require_features failed");
return -1;
}
base = event_base_new_with_config(evconfig);
if (!base) {
log_error("ttypread:event_base_new failed");
return -1;
}
const char* method; //these 3 lines are the new line edited
method = event_base_get_method(base);
log_debug("ttyread is using method = %s",method);
ev = event_new(base, fd, EV_READ|EV_PERSIST, ttypread_event_cb, &user_data);
c = event_add(ev, NULL);
log_debug("ttypread passed event_add with c value is %d",c);
in_buffer = bufferevent_socket_new(base, STDIN_FILENO, BEV_OPT_CLOSE_ON_FREE);
log_debug("ttypread passed bufferevent_socket_new");
if(in_buffer == NULL){
log_debug("problem with bufferevent_socket_new");
}
bufferevent_setcb(in_buffer, in_read_cb, NULL, in_event_cb, NULL);
bufferevent_disable(in_buffer, EV_WRITE);
bufferevent_enable(in_buffer, EV_READ);
k =event_base_dispatch(base);
log_debug("event_base have been dispatched"); //when looking at the debugging file, the other plaform will go to ttypread_event_cb function. But for hpux itanium, it stays here.
if (k == 0){
log_debug("event_base_dispatch returned 0");
} else if (k == -1){
log_debug("event_base_dispatch returned -1");
} else {
log_debug("event_base_dispatch returned 1");
}
event_base_free(base);
event_free(ev);
log_debug("finish ttypread");
log_debug("ttypread_ret will return [%d]",ttypread_ret);
return ttypread_ret;
}
void ttypread_event_cb(evutil_socket_t fd, short events, void *arg)
{
int nread;
struct timeval t;
struct user_data *user_data;
user_data = (struct user_data*)arg;
nread = 0;
log_debug("inside ttypread_event_cb");
if (events & EV_READ) {
log_debug("got events & EV_READ");
nread = ttyread(fd, user_data->h, user_data->buf);
if (nread == -1) {
ttypread_ret = -1;
event_del(ev);
event_base_loopexit(base, NULL);
} else if (nread == 0) {
if (access(input_filename, F_OK)!=0) {
log_debug("cannot access [%s]",input_filename);
tcsetattr(0, TCSANOW, &old); /* Return terminal state */
exit(EXIT_SUCCESS);
}
t.tv_sec = 0;
t.tv_usec = 250000;
select(0, 0, 0, 0, &t);
} else {
ttypread_ret = 1;
event_del(ev);
event_base_loopexit(base, NULL);
}
}
else if (events & EV_WRITE) {
log_debug("got events & EV_WRITE");
}
}
Not sure if this help. But just some info on the hpux itanium
uname -a = HP-UX hpux-ita B.11.23 U ia64
If you need any additional info or other declaration on function, just leave a comment and I will edit the question.
EDIT : i've added a function inside ttypread. Somehow for hpux itanium its returning devpoll while other platform are returning poll. Im not sure if this is the problem. But if that is so, is there any way for me to change it?
After checking the result from event_base_get_method, I found out that only on my hpux-itanium used devpoll method. This is how I solve it.
char string[8] = "devpoll";
struct user_data user_data;
struct bufferevent *in_buffer;
struct event_config *evconfig;
const char *method;
const char *devpoll;
devpoll = string;
in_buffer = NULL;
user_data.fd = fd;
user_data.h = h;
user_data.buf = buf;
evconfig = event_config_new();
if (evconfig == NULL) {
log_error("event_config_new failed");
return -1;
}
if (event_config_require_features(evconfig, EV_FEATURE_FDS)!=0) {
log_error("event_config_require_features failed");
return -1;
}
if (event_config_avoid_method(evconfig,devpoll) != 0)
{
log_error("Failed to ignore devpoll method");
}
Force the libevent to ignore using devpoll and use poll instead.

Serving large files (>2GB) with libevent on 32-bit system

Preamble: lightweight http server written in C based on libevent v2 (evhttp), Linux, ARM, glibc2.3.4
I'm trying to serve big files (over 2GB) using evbuffer_add_file() on 32 bit system.
The libevent was compiled with -D_FILE_OFFSET_BITS=64 flag. Here is the simplified code:
int fd = -1;
if ((fd = open(path, O_RDONLY)) < 0) {
// error handling
}
struct stat st;
if (fstat(fd, &st) < 0) {
// error handling
}
struct evbuffer *buffer = evbuffer_new();
evbuffer_set_flags(buffer, EVBUFFER_FLAG_DRAINS_TO_FD); // force using system's sendfile
evbuffer_add_file(buffer, fd, 0, st.st_size);
evhttp_send_reply(req, 200, NULL, buffer);
evbuffer_free(buffer);
st.st_size has correct value, in this case 4913809524, but response header Content-Length has value of 618842228. Even if i set Content-Length header to appropriate value the file transfer stops at 618842228 ...
Do i miss or do something wrong? Is it possible at all?
Thanks in advance
As i said in comment obviously the problem is not in libevent, but in system's sendfile implementation. So with a little workaround i found the way to solve this problem using
evhttp_send_reply_(start|chunk|end) functions family:
struct chunk_req_state {
struct evhttp_request *req;
int fd;
long chunksize;
off_t filesize;
off_t offset;
};
static void
chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
{
struct evbuffer *evb = evbuffer_new();
struct chunk_req_state *state = arg;
struct timeval when = { 0, 0 };
ev_ssize_t read;
if (lseek(state->fd, state->offset, SEEK_SET) == -1) {
evbuffer_free(evb);
close(state->fd);
free(state);
return;
}
read = evbuffer_read(evb, state->fd, (ev_ssize_t) state->chunksize);
if (read == -1) {
evbuffer_free(evb);
evhttp_send_reply_end(state->req);
close(state->fd);
free(state);
return;
}
evhttp_send_reply_chunk(state->req, evb);
evbuffer_free(evb);
state->offset += read;
if (state->offset < state->filesize) {
// there's more data to send
event_base_once(ebase, -1, EV_TIMEOUT, chunked_trickle_cb, state, &when);
} else {
// reached the end
evhttp_send_reply_end(state->req);
close(state->fd);
free(state);
}
}
int fd = -1;
if ((fd = open(path, O_RDONLY)) < 0) {
// error handling
}
struct stat st;
if (fstat(fd, &st) < 0) {
// error handling
}
struct timeval when = { 0, 0 };
struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
memset(state, 0, sizeof(struct chunk_req_state));
state->req = req;
state->fd = fd;
state->chunksize = 10*1024*1024;
state->filesize = st.st_size;
state->offset = 0;
// set Content-Length to prevent chunked transfer
char *length = NULL;
spprintf(&length, 0, "%lld", st.st_size);
evhttp_add_header(evhttp_request_get_output_headers(request->req), "Content-Length", length);
free(length);
evhttp_send_reply_start(request->req, 200, NULL);
event_base_once(ebase, -1, EV_TIMEOUT, chunked_trickle_cb, state, &when);

Resources