Related
We are working on a project where we want to communicate with a server.
This is our function to communicate with the server, but somehow it does not read the incoming messages correctly all the time.
Sometimes in the buffer there is something like:
(Server sends)"+ Client version acClient: ID 38ail6ii3s8jc"
instead of:
(Server sends)"+ Client version accepted - please send Game-ID to join"
(We send)"Client: ID 38ail6ii3s8jc"
So, I think the error is within the char *receiveAnswer(int sock) function.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#define BUFFERSIZE 1024
#define bzeroNew(b,len) (memset((b), '\0', (len)), (void) 0) //buffer loeschen
#define VERSION "VERSION 3.4\n"
#include "functions.h"
char buffer[BUFFERSIZE];
int prologEnd = 0;
int proof;
//liest von Server eine Nachricht ein und speichert sie im buffer ab
char *receiveAnswer(int sock) {
bzeroNew(buffer, BUFFERSIZE);
if(recv(sock, buffer, sizeof(buffer), 0) < 0) {
perror("ERROR: Empfangen fehlgeschlagen\n");
}
printf("%s", buffer);
return buffer;
}
void sendResponse(int sock, char* message) {
bzeroNew(buffer, BUFFERSIZE);
strcpy(buffer, message);
proof = send(sock, buffer, strlen(buffer), 0);
if(proof < 0) {
perror("ERROR: Senden fehlgeschlagen\n");
}
printf("Client: %s\n", buffer);
receiveAnswer(sock);
}
int performConnection(int sock, char* gameID) {
bzeroNew(buffer, BUFFERSIZE);
receiveAnswer(sock);
while(strncmp(buffer, "+", 1) == 0 && prologEnd == 0) {
if(strncmp(buffer, "+ MNM Gameserver", 16) == 0) {
receiveAnswer(sock);
sendResponse(sock, VERSION);
}
else if(strncmp(buffer, "+ Client", 8) == 0) {
sendResponse(sock, gameID);
}
else if(strncmp(buffer, "+ PLAYING", 9) == 0) {
sendResponse(sock, "PLAYER\n");
receiveAnswer(sock);
}
else if(strncmp(buffer, "+ YOU", 5) == 0) {
receiveAnswer(sock);
printf("\n");
prologEnd = 1;
}
else if(strncmp(buffer, "+ TOTAL", 7) == 0) {
receiveAnswer(sock);
receiveAnswer(sock);
prologEnd = 1;
}
}
bzeroNew(buffer, BUFFERSIZE);
return 0;
}
This is our main() function, but I think the error is within the file above:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h> // für Warten auf Kindprozess
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
// für Shared Memory:
#include <sys/ipc.h>
#include <sys/shm.h>
#include "functions.h"
#include "sharedMemory.h"
// dublicat, brauchen wir das?
#define GAMEKINDNAME "NMMorris"
#define HOSTNAME "sysprak.priv.lab.nm.ifi.lmu.de"
#define PORTNUMBER 1357
int main (int argc, char *argv[]) {
char gamekindname[256] = "NMMorris";
char hostname[256] = "sysprak.priv.lab.nm.ifi.lmu.de";
int portnumber = 1357;
char* gameID = argv[2];
char playerNumber[256];
char configFile[256] = "client.conf" ;
int fd[2]; // TODO: fd und client_fd vereinen
//gameID formatieren
char bufferGameID[64];
strcpy(bufferGameID, "ID ");
strcat(bufferGameID, gameID);
strcpy(gameID, bufferGameID);
strcat(gameID, "\n");
int i;
char tmp[256];
//Argumente einlesen und an Variablen übergeben
for(i = 3; i < 7; i++) {
strcpy(tmp, argv[i]);
if (strcmp(tmp, "-p") == 0){
strcpy(playerNumber, argv[i+1]);
} else if (strcmp(tmp, "-conf") == 0){
strcpy(configFile, argv[i+1]);
}
}
config configMain = readConfig(configFile);
strcpy(gamekindname, configMain.gameKind);
strcpy(hostname, configMain.hostServerName);
portnumber = configMain.portNmbr;
printf("\n>>>Config File Data<<<\n");
printf("HostServerName: %s\n", hostname);
printf("PortNumber: %d\n", portnumber);
printf("GameKind: %s\n\n ", gamekindname);
//From here: sockets
int sock, client_fd;
struct sockaddr_in serv_addr;
struct hostent *server;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("\nERROR: Socket creation error \n");
return - 1;
}
//ipAdresse nachschauen
server = gethostbyname(hostname);
if (server == NULL)
{
perror("ERROR: no such host\n");
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portnumber);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
if ((client_fd = connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))) < 0) {
perror("ERROR: Connection Failed \n");
return -1;
}
printf(">>> Mit Host : %s verbunden <<<\n", hostname);
if(performConnection(sock, gameID) != 0) {
perror("performConnection Failed\n");
} // TODO: verlagern
close(client_fd);
return 0;
// Shared Memory Segment erstellen
int shmid_game = shmget(KEY, sizeof(gameInfo), IPC_CREAT | SHM_R | SHM_W);
if (shmid_game == -1) {
perror("Error while creating shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Creation successful\n");
}
int shmid_player = shmget(KEY, sizeof(playerInfo), IPC_CREAT | SHM_R | SHM_W);
if (shmid_player == -1) {
perror("Error while creating shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Creation successful\n");
}
// Prozess mit SHM verbinden
void* shm_game = shmat(shmid_game, 0, 0);
if (shm_game == NULL) {
perror("Error while attaching shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Attachment successful\n");
}
void* shm_player = shmat(shmid_player, 0, 0);
if (shm_player == NULL) {
perror("Error while attaching shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Attachment successful\n");
}
// Kindprozess (Connector) erstellen
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fehler bei Erstellung des Kindprozesses.\n");
} else if (pid == 0) { // Kindprozess (Connector)
close(fd[1]);
performConnection(sock, gameID);
} else { // Elternprozess (Thinker)
close(fd[0]);
}
return 0;
}
TCP is a stream-oriented protocol, not a message-oriented one. A message sent as 100 bytes can be received as 1 100-byte read or as 100 1-byte reads, or any combination in between. This means that you must keep looping on the recv() until you have the whole message. That, in turn, means you need to know when a message is completely received. Either prepend the message's length before the message, use a fixed-size message, or have a unique recognizable terminator at the end of the message.
recv() returns the number of bytes that it has written into your buffer -- which is to say, it returns the number of bytes that it currently has available to give to you at the time you called it. Importantly, that will often be less than the number of bytes you requested, so it is mandatory that you check the return value of recv() to find out how many bytes you actually received, and not just assume that the value returned by recv() is equal to sizeof(buffer).
OTOH if you want recv() to not return until sizeof(buffer) bytes have been successfully read, you can pass the MSG_WAITALL flag to recv() in the fourth argument.
From the recv man page:
This flag requests that the operation block until the full
request is satisfied. However, the call may still return
less data than requested if a signal is caught, an error
or disconnect occurs, or the next data to be received is
of a different type than that returned. This flag has no
effect for datagram sockets.
It is not guaranteed that recv() will get all of the bytes sent at once. The convention is to call it in a loop until you've read all the bytes.
NB that recv() returns 0 when the client is stalling or closed the connection, and -1 on a read error.
Handling partial send()s:
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) { break; }
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n==-1?-1:0; // return -1 on failure, 0 on success
}
— From Beej's guide to Network Programming
The above code snippet calls send() in a loop until all the data has been sent.
You can now write a similar recv_all function that calls recv() in a loop until it has read all the data.
Handling partial recv()s:
Perhaps something like this:
/* Synopsis: Calls recv() in a loop to ensure
* len bytes have been read. Stores
* the total number of bytes sent in len.
*
* Returns: 0 on failure, 1 otherwise.
*/
static int recv_all(int sockfd, char *buf, size_t *len)
{
size_t bytes_left = *len;
size_t total = 0;
ssize_t rv = 0;
errno = 0;
while (total < *len) {
rv = recv(sockfd, buf + total, bytes_left, 0);
if (rv == 0) { /* Client closed the connection or is stalling */
return 0;
} else if (rv == -1) { /* A read error */
perror("recv");
return 0;
}
total += rv;
bytes_left -= rv;
}
*len = total;
return 1;
}
recv() may also return 0 when 0 characters were read. In that case, len can be compared against the original len to see if the call to recv() was successful.
Note: The above recv_all function has not been tested, and hence is not guaranteed to be bug free. It's meant to be an example.
I am trying check new event created or deleted in the specific directory.
If event is created then need to do some operation using thread and this thread should be in initiate wile loop .
Main thread should look for another event creation or deletion.
I have developed application , but I could not able to second event is created or not.
Code flow is always in child thread.
Please find below code :
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#define PATH_MAX 4096
#define O_RDONLY 00
int main()
{
//char buf;
char buf[4096]
__attribute__ ((aligned(__alignof__(struct inotify_event))));
int fd, i, poll_num;
int *wd;
nfds_t nfds;
struct pollfd fds[0];
char *device_path = "/home/home/test";
ssize_t len;
const struct inotify_event *event;
int counter=0;
char getdata[10];
char devname[PATH_MAX];
char *filename;
int thread_ret = 0;
static pthread_t inotify_pthread;
/* Create the file descriptor for accessing the inotify API */
fd = inotify_init1(IN_NONBLOCK);
printf("fd = %d",fd);
if (fd == -1) {
perror("inotify_init1");
exit(EXIT_FAILURE);
}
/* Mark directories for events
- file was opened
- file was closed */
wd = inotify_add_watch(fd, device_path,
IN_OPEN | IN_CLOSE | IN_CREATE | IN_DELETE);
printf("wd = %d ",wd);
if (wd == -1) {
fprintf(stderr, "Cannot watch '%s': %s\n",
device_path, strerror(errno));
exit(EXIT_FAILURE);
}
/* Prepare for polling */
nfds = 1;
/* Inotify input */
fds[0].fd = fd;
fds[0].events = POLLIN;
/* Wait for events and/or terminal input */
printf("Listening for events.\n");
while (1) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR)
continue;
perror("poll");
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[0].revents & POLLIN) {
/* Inotify events are available */
len = read(fd, buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
if (len <= 0)
break;
for (char *ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len) {
event = (struct inotify_event *) ptr;
if ( event->len ) {
/* Print event type */
if (event->mask & IN_CREATE){
printf("IN_CREATE: \n");
if (event->len){
counter++;
printf("IN_CREATE: created file name : %s , counter no = %d \n", event->name,counter);
thread_ret = pthread_create(&inotify_pthread, NULL, event_read_data(),NULL);
if (thread_ret < 0) {
printf("can't create inotify thread");
exit(EXIT_FAILURE);
}
}
}
else if (event->mask & IN_DELETE){
counter--;
printf( "IN_DELETE : Deleted File name %s , counter no = %d \n", event->name,counter );
}
}
printf("counter number : %d \n",counter);
}
}
}
}
printf("Listening for events stopped.\n");
close(fd);
free(wd);
exit(EXIT_SUCCESS);
}
event_read_data(){
while(1){
printf("Child Thread");
sleep(3);
}
}
Compiled like below:
gcc fine_name.c -lpthread
I am trying to read a FIFO in the background (using a thread) while my main program is running inside an infinite loop. I want to use select() because otherwise the processor runs at 100%, but the appropriate example I found isn't working. This is the example code:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
mkfifo("FIFO", 0666);
fd_set readCheck;
fd_set errCheck;
char buffer[64];
struct timeval timeout;
int rv;
int fd = open("FIFO", O_RDONLY | O_RSYNC);
FD_ZERO(&readCheck);
FD_ZERO(&errCheck);
while (1) {
FD_SET(fd, &readCheck);
FD_SET(fd, &errCheck);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
rv = select(fd, &readCheck, NULL, &errCheck, &timeout);
if (rv < 0) {
printf("Select failed\r\n");
break;
}
if (FD_ISSET(fd, &errCheck)) {
printf("FD error\r\n");
continue;
}
if (FD_ISSET(fd, &readCheck)) {
memset(buffer, 0, sizeof(buffer));
rv = read(fd, buffer, sizeof(buffer));
if (rv < 0) {
printf("Read failed\r\n");
break;
}
printf(buffer);
buffer[64] = '\0';
}
}
close(fd);
return 0;
}
When I write to the FIFO file nothing happens, but using cat FIFO prints the contents. What can be the problem?
You have to give the first argument as one higher than the last opened file descriptor. From the man page page of select,
nfds is the highest-numbered file descriptor in any of the three sets, plus 1
Change this line,
rv = select(fd, &readCheck, NULL, &errCheck, &timeout);
to
rv = select(fd+1, &readCheck, NULL, &errCheck, &timeout);
If you are not mentioning this,then select will not check your descriptor so you will not the file descriptor is ready for reading.
Your buffer is declared as
char buffer[64];
using
buffer[64] = '\0';
you are writing out of bounds.
Change
buffer[64] = '\0';
to
buffer[rv] = '\0';
update 20-12-2014: This problem has been solved, see the bottom of the question for working code.
Design
There are four clients that process some data and then passes it to a server process over a named pipe (FIFO).
Problem
When running the server outside of gdb (not stepping in gdb also gives the same problem) only one pipe is read. Select returns 1 and FD_ISSET only reacts to one pipe (and it stays the same pipe during execution).
Looking into /proc/[PID]/{fd,fdinfo} shows that the other pipes are still open and haven't been read. The pos field in fdinfo is 0).
The Question
What do I need to change to read from all the four pipes in an interleaved fashion?
Test
To simulate the client I use a 12MByte random file that is catted onto the named pipe.
The random file is generated with:
dd if=/dev/urandom of=test.bin bs=1024 count=$((1024*12))
And then executed as (each in a separate terminal and in the following order)
terminal 1:
./server.out
terminal 2:
cat test.bin > d0
terminal 3:
cat test.bin > d1
terminal 4:
cat test.bin > d2
terminal 5:
cat test.bin > d3
Makefile
server:
gcc server.c -o server.out -g -D _DEFAULT_SOURCE -Wall --std=c11
Source
The clients are called dongles.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define NR_OF_DONGLES 4
int do_something(int fd);
int main()
{
fd_set read_fd_set;
FD_ZERO(&read_fd_set);
int dongles[NR_OF_DONGLES];
/*Create FIFO */
for(int i = 0; i < NR_OF_DONGLES; i++)
{
char name[255];
snprintf(name, sizeof(name), "d%d", i);
if(mkfifo(name, 0666) == -1)
{
fprintf(stderr, "Failed to create fifo %s \t Error: %s", name, name);
exit(EXIT_FAILURE);
}
int dongle = open(name, O_RDONLY);
if(dongle > 0)
{
fprintf(stderr,"set dongle %s\n", name);
FD_SET(dongle, &read_fd_set);
dongles[i] = dongle;
}
else
{
fprintf(stderr, "failed to open: %s\nerror: %s\n", name, strerror(errno));
exit(EXIT_FAILURE);
}
}
int closed = 0;
int isset[NR_OF_DONGLES];
memset(isset, 0, sizeof(isset));
while(closed < NR_OF_DONGLES)
{
int active;
if((active = select (FD_SETSIZE , &read_fd_set, NULL,NULL,NULL)) < 0)
{
fprintf(stderr, "select failed\n errno: %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stderr, "active devices %i\n", active);
for(int i = 0; i < NR_OF_DONGLES; ++i)
{
int dongle = dongles[i];
if(FD_ISSET(dongle, &read_fd_set))
{
isset[i] += 1;
int size = do_something(dongle);
fprintf(stderr, "round %i \tdongle %i \tread %i bytes\n", isset[i],i, size);
if(size == 0)
{
if(close(dongle) == -1)
{
fprintf(stderr,"Could not close dongle %i\nError: %s\n",
i,strerror(errno));
}
closed += 1;
fprintf(stderr, "closed dongle %i \t number of closed dongles %i\n",
i, closed);
FD_CLR(dongle, &read_fd_set);
}
}
}
}
exit(EXIT_SUCCESS);
}
#define BLOCK_SIZE (8*1024)
/*
* If the size is zero we reached the end of the file and it can be closed
*/
int do_something(int fd)
{
int8_t buffer[BLOCK_SIZE];
ssize_t size = read(fd, buffer, sizeof(buffer));
if(size > 0)
{
//Process read data
}
else if(size == -1)
{
fprintf(stderr, "reading dongle failed\nerrno: %s", strerror(errno));
return -1;
}
return size;
}
The Solution
kestasx's solution worked for me. The watchlist (read_fd_set) needs to be reinitialized before a call to select.
Source code
while(closed < number_of_dongles)
{
/*Reinitialize watchlist of file descriptors.*/
FD_ZERO(&read_fd_set);
for(int i = 0; i < number_of_dongles; i++)
{
int dongle = dongles[i];
/*if fd == -1 the pipe has been closed*/
if(dongle != -1)
{
FD_SET(dongle, &read_fd_set);
}
}
int active = select (FD_SETSIZE , &read_fd_set, NULL,NULL,NULL);
if(active < 0)
{
fprintf(stderr, "select failed\n errno: %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
//fprintf(stderr, "active devices %i\n", active);
for(int i = 0; i < number_of_dongles; ++i)
{
int dongle = dongles[i];
/*Check if the current dongle fd has data in the FIFO*/
if(FD_ISSET(dongle, &read_fd_set))
{
isset[i] += 1;
int size = transfer_dongle_data(dongle);
// fprintf(stderr, "round %i \tdongle %i \tread %i bytes\n", isset[i],i, size);
if(size == 0)
{
if(close(dongle) == -1)
{
fprintf(stderr,"Could not close dongle %i\nError: %s\n",
i,strerror(errno));
}
closed += 1;
fprintf(stderr, "closed dongle %i \t number of closed dongles %i\n",
i, closed);
FD_CLR(dongle, &read_fd_set); //could be removed
/*notify that the pipe is closed*/
dongles[i] = -1;
}
}
}
}
You can try to run Your code via strace (truss on Solaris, ktrace/kdump on FreeBSD). For me it stalls on open("d0", O_RDONLY). So the server dosn't create all pipes before (other pipes most likely are created by cat).
if(dongle)... after open is incorrect: in case of failure open() returns -1, not 0.
Because of this, I think Your program is not working with files You expect (only one pipe is openned correctly).
One more issue ir related to select() use. You should reinitialize read_fd_set before each call to select(), because after each select() call only descriptors, which have data are left marked, others are cleared.
After some tinkering, I got it. On my MacOSX open(...) would block until there was actually something on the FIFO. Consider the program below; it worked as soon as you start feeding data into d0, d1 and so on. But until then, the output of the program was only:
Creating dongle d0
Creating dongle d1
Creating dongle d2
Creating dongle d3
Opening dongle d0
So I pulled up the manpage for open() and lo and behold - there's a flag O_NONBLOCK. With that added in, the following code works like a charm. FYI, POSIX doesn't say that opening a FIFO should block, but I found comments that some implementations do.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define NR_OF_DONGLES 4
#define BLOCK_SIZE (8*1024)
// Prints error and stops program
int error(char const *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
exit(1);
}
// Alternative for printf() that flushes stdout
void msg(char const *fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
fflush(stdout);
}
// Reads an open readable fd
int do_something(int fd) {
int8_t buffer[BLOCK_SIZE];
ssize_t size = read(fd, buffer, sizeof(buffer));
if(size > 0)
msg("Got data from fd %d, length %ld\n", fd, size);
else if (size == -1)
error("Reading dongle fd %d failed: %s\n", fd, strerror(errno));
else
msg("Dongle with fd %d signals EOF\n", fd);
return size;
}
int main() {
int dongles[NR_OF_DONGLES];
// Create the dongles, open, add to list of open fd's
for (int i = 0; i < NR_OF_DONGLES; i++) {
char name[255];
snprintf(name, sizeof(name), "d%d", i);
msg("Creating dongle %s\n", name);
if (mkfifo(name, 0666) == -1 && errno != EEXIST)
error("Failed to create fifo %s: %s\n", name, strerror(errno));
}
for (int i = 0; i < NR_OF_DONGLES; i++) {
char name[255];
snprintf(name, sizeof(name), "d%d", i);
msg("Opening dongle %s\n", name);
/* ****************************************
* Here it is, first test was with
* int fd = open(name, O_RDONLY);
* which blocked at the open() call
*******************************************/
int fd = open(name, O_RDONLY | O_NONBLOCK);
if (fd < 0)
error("Cannot open dongle %s: %s\n", name, strerror(errno));
dongles[i] = fd;
}
int closed = 0;
while (closed < NR_OF_DONGLES) {
msg("Closed dongles so far: %d\n", closed);
// Add dongle fd's to select set, unless the fd is already closed
// which is indicated by fd == -1
fd_set read_fd_set;
FD_ZERO(&read_fd_set);
for (int i = 0; i < NR_OF_DONGLES; i++)
if (dongles[i] > 0)
FD_SET(dongles[i], &read_fd_set);
// Wait for readable fd's
int active;
if ( (active = select (FD_SETSIZE , &read_fd_set, 0, 0, 0)) < 0 )
error("Select failure: %s\n", strerror(errno));
msg("Active dongles: %d\n", active);
for (int fd = 0; fd < FD_SETSIZE; fd++)
if (FD_ISSET(fd, &read_fd_set)) {
msg("Fd %d is readable\n", fd);
int size = do_something(fd);
if (!size) {
// Fd signals EOF. Close dongle, remove from array
// of open fd's by setting to -1
msg("Fd %d signals EOF\n", fd);
if (close(fd) < 0)
error("Failure to close fd %d: %s\n",
fd, strerror(errno));
for (int i = 0; i < NR_OF_DONGLES; i++)
if (dongles[i] == fd)
dongles[i] = 0;
// Update closed fd counter
closed++;
}
}
}
exit(0);
}
I want to send a opened file descriptor between two different programs. So I am using ioctl with named pipes to do so. But there I am getting "Invalid argument" error for ioctl().
#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#define MSGSIZ 63
char *fifo = "fifo";
int send_err(int fd, int errcode, const char *msg)
{
int n;
if ((n = strlen(msg)) > 0)
if (write(fd, msg, n) != n) /* send the error message */
return(-1);
if (errcode >= 0)
errcode = -1; /* must be negative */
if (send_fd(fd, errcode) < 0)
return(-1);
return(0);
}
int send_fd(int fd, int fd_to_send)
{
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
buf[0] = 0; /* null byte flag to recv_fd() */
if (fd_to_send < 0) {
buf[1] = -fd_to_send; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
buf[1] = 0; /* zero status means OK */
}
//printf("From the write %d\n",buf[0]);
if (write(fd, buf, 2) != 2)
return(-1);
if (fd_to_send >= 0)
if (ioctl(fd, I_SENDFD, fd_to_send) < 0)
{
printf("Eroor ::: %s\n",strerror(errno));
return(-1);
}
return(0);
}
int main(int argc, char const *argv[])
{
int fd, j, nwrite;
char msgbuf[MSGSIZ+1];
int fd_to_send;
if((fd_to_send = open("vi",O_RDONLY)) < 0)
printf("vi open failed");
if(argc < 2)
{
fprintf(stderr, "Usage: sendmessage msg ... \n");
exit(1);
}
/* open fifo with O_NONBLOCK set */
if((fd = open(fifo, O_WRONLY | O_NONBLOCK)) < 0)
printf("fifo open failed");
/* send messages */
for (j = 1; j < argc; j++)
{
if(strlen(argv[j]) > MSGSIZ)
{
fprintf(stderr, "message too long %s\n", argv[j]);
continue;
}
strcpy(msgbuf, argv[j]);
if((nwrite = write(fd, msgbuf, 6)) == -1)
printf("message write failed");
}
printf("From send_fd %d \n",send_fd(fd,fd_to_send));
exit(0);
}
The file accessories .h only contain some common include files nothing else.
First I am sending a simple message and then calling send_fd which is first sending a 2 byte message and then have to send file descriptor using ioctl(). But it is not.
It looks like linux doesn't support I_SENDFD. The comments indicate that I_SENDFD is in the documentation, but is not actually supported, and results in the error message you encountered. The wikipedia entry for STREAMS states the linux kernel does not have any support for streams. The wikipedia entry does point to a couple of third-party packages that could be used to add streams support, but LiS has not been ported to the 2.6 kernel, and OpenSS7 hasn't had any active development in 4 years.
However, linux does support something similar. This mechanism uses a special message type SCM_RIGHTS to deliver a file descriptor over a UNIX domain socket with sendmsg and obtained from recvmsg. Examples can be found with a simple web search, a complete example seems to be from the book The Linux Programming Interface, with source for sending and receiving.