I'm playing with SOCK_SEQPACKET type on Unix sockets.
The code I'm using for reading is the classic
ssize_t recv_size = recv(sd, buffer, sizeof(buffer), 0);
if (recv_size < 0) {
handle_error("recv", errno);
} else if (recv_size > 0) {
handle_packet(buffer, recv_size);
} else {
// recv_size == 0 => peer closed socket.
handle_end_of_stream();
}
While this works just fine, I noticed it is not able to distinguish between a socket closure and a message of size 0. In other words, if on the other end I issue a sequence of calls like this:
send(sd, "hello", strlen("hello"), 0);
send(sd, "", 0, 0);
send(sd, "world", strlen("world"), 0);
…the reader will only receive "hello" and react to the second message with a socket closure, missing the "world" message entirely.
I was wondering if there's some way to disambiguate between the two situations.
What if you make some sort of "confirmation" function on both ends. For example instead of handle_end_of_stream, make something like this:
->send(xx, "UNIQUE_MESSAGE", strlen("UNIQUE_MESSAGE"), 0);
< you receive "UNIQUE_RESPONSE" if connection is still up, if you don't, you know for sure that the other end is closed. Just filter out some sort of "UNIQUE_MESSAGE" and "UNIQUE_RESPONSE" inf your "confirmation" function.
As I mentioned in a comment, zero-length seqpackets (as well as zero-length datagrams) can behave oddly, typically mistaken for disconnects; and for that reason, I definitely recommend against using zero-length seqpackets or datagrams for any purpose.
To illustrate the main problem, and explore the details, I created two test programs. First is receive.c, which listens on an Unix domain seqpacket socket, accepts one connection, and describes what it receives:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = 1;
}
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;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static inline unsigned int digit(const int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
default: return 16;
}
}
static inline unsigned int octbyte(const char *src)
{
if (src) {
const unsigned int o0 = digit(src[0]);
if (o0 < 4) {
const unsigned int o1 = digit(src[1]);
if (o1 < 8) {
const unsigned int o2 = digit(src[2]);
if (o2 < 8)
return o0*64 + o1*8 + o2;
}
}
}
return 256;
}
static inline unsigned int hexbyte(const char *src)
{
if (src) {
const unsigned int hi = digit(src[0]);
if (hi < 16) {
const unsigned int lo = digit(src[1]);
if (lo < 16)
return 16*hi + lo;
}
}
return 256;
}
size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
char *dst = addr->sun_path;
char *const end = addr->sun_path + sizeof (addr->sun_path) - 1;
unsigned int byte;
if (!src || !addr)
return 0;
memset(addr, 0, sizeof *addr);
addr->sun_family = AF_UNIX;
while (*src && dst < end)
if (*src == '\\')
switch (*(++src)) {
case '0':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else {
*(dst++) = '\0';
src++;
}
break;
case '1':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '2':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '3':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'x':
byte = hexbyte(src + 1);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'a': *(dst++) = '\a'; src++; break;
case 'b': *(dst++) = '\b'; src++; break;
case 't': *(dst++) = '\t'; src++; break;
case 'n': *(dst++) = '\n'; src++; break;
case 'v': *(dst++) = '\v'; src++; break;
case 'f': *(dst++) = '\f'; src++; break;
case 'r': *(dst++) = '\r'; src++; break;
case '\\': *(dst++) = '\\'; src++; break;
default: *(dst++) = '\\';
}
else
*(dst++) = *(src++);
*(dst++) = '\0';
return (size_t)(dst - (char *)addr);
}
int main(int argc, char *argv[])
{
struct sockaddr_un addr, conn;
socklen_t addrlen, connlen;
int socketfd, connfd;
if (argc != 2 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s SOCKET_PATH\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
socketfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (socketfd == -1) {
fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
addrlen = set_unix_path(argv[1], &addr);
if (bind(socketfd, (const struct sockaddr *)&addr, addrlen) == -1) {
fprintf(stderr, "Cannot bind to %s: %s.\n", argv[1], strerror(errno));
close(socketfd);
return EXIT_FAILURE;
}
if (listen(socketfd, 1) == -1) {
fprintf(stderr, "Cannot listen for incoming connections: %s.\n", strerror(errno));
close(socketfd);
return EXIT_FAILURE;
}
memset(&conn, 0, sizeof conn);
connlen = sizeof conn;
connfd = accept(socketfd, (struct sockaddr *)&conn, &connlen);
if (connfd == -1) {
close(socketfd);
fprintf(stderr, "Canceled.\n");
return EXIT_SUCCESS;
}
if (connlen > 0)
fprintf(stderr, "Connected, peer address size is %d.\n", (int)connlen);
else
fprintf(stderr, "Connected; no peer address.\n");
while (!done) {
char buffer[65536];
ssize_t n;
int r;
n = recv(connfd, buffer, sizeof buffer, 0);
if (n > 0)
fprintf(stderr, "Received %zd bytes.\n", n);
else
if (n == 0) {
struct pollfd fds[1];
fds[0].fd = connfd;
fds[0].events = 0;
fds[0].revents = 0;
r = poll(fds, 1, 0);
if (r > 0 && (fds[0].revents & POLLHUP)) {
fprintf(stderr, "Disconnected (revents = %d).\n", fds[0].revents);
break;
} else
if (r > 0)
fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents);
else
if (r == 0)
fprintf(stderr, "Received a zero-byte seqpacket.\n");
else
fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents);
}
}
close(connfd);
close(socketfd);
return EXIT_SUCCESS;
}
You can compile the above using e.g. gcc -Wall -O2 receive.c -o receive. To run, give it the Unix domain address to listen on. In Linux, you can use the abstract namespace by prepending \0 to the address; for example, by running ./receive '\0example' . Otherwise, the socket address will be visible in the filesystem, and you'll need to remove it (as if it was a file, using rm) before running ./receive again with the same socket address.
We also need an utility to send seqpackets. The following send.c is very similar (reuses much of the same code). You specify the Unix domain address to connect to, and one or more seqpacket lengths. You can also specify delays in milliseconds (just prepend a -; i.e., negative integers are delays in milliseconds):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = 1;
}
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;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static inline unsigned int digit(const int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
default: return 16;
}
}
static inline unsigned int octbyte(const char *src)
{
if (src) {
const unsigned int o0 = digit(src[0]);
if (o0 < 4) {
const unsigned int o1 = digit(src[1]);
if (o1 < 8) {
const unsigned int o2 = digit(src[2]);
if (o2 < 8)
return o0*64 + o1*8 + o2;
}
}
}
return 256;
}
static inline unsigned int hexbyte(const char *src)
{
if (src) {
const unsigned int hi = digit(src[0]);
if (hi < 16) {
const unsigned int lo = digit(src[1]);
if (lo < 16)
return 16*hi + lo;
}
}
return 256;
}
size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
char *dst = addr->sun_path;
char *const end = addr->sun_path + sizeof (addr->sun_path) - 1;
unsigned int byte;
if (!src || !addr)
return 0;
memset(addr, 0, sizeof *addr);
addr->sun_family = AF_UNIX;
while (*src && dst < end)
if (*src == '\\')
switch (*(++src)) {
case '0':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else {
*(dst++) = '\0';
src++;
}
break;
case '1':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '2':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case '3':
byte = octbyte(src);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'x':
byte = hexbyte(src + 1);
if (byte < 256) {
*(dst++) = byte;
src += 3;
} else
*(dst++) = '\\';
break;
case 'a': *(dst++) = '\a'; src++; break;
case 'b': *(dst++) = '\b'; src++; break;
case 't': *(dst++) = '\t'; src++; break;
case 'n': *(dst++) = '\n'; src++; break;
case 'v': *(dst++) = '\v'; src++; break;
case 'f': *(dst++) = '\f'; src++; break;
case 'r': *(dst++) = '\r'; src++; break;
case '\\': *(dst++) = '\\'; src++; break;
default: *(dst++) = '\\';
}
else
*(dst++) = *(src++);
*(dst++) = '\0';
return (size_t)(dst - (char *)addr);
}
static inline long sleep_ms(const long ms)
{
struct timespec t;
if (ms > 0) {
t.tv_sec = ms / 1000;
t.tv_nsec = (ms % 1000) * 1000000;
if (nanosleep(&t, &t) == -1 && errno == EINTR)
return 1000 * (unsigned long)(t.tv_sec)
+ (unsigned long)(t.tv_nsec / 1000000);
return 0;
} else
return ms;
}
static int parse_long(const char *src, long *dst)
{
const char *end = src;
long val;
if (!src || !*src)
return errno = EINVAL;
errno = 0;
val = strtol(src, (char **)&end, 0);
if (errno)
return errno;
if (end == src)
return errno = EINVAL;
while (*end == '\t' || *end == '\n' || *end == '\v' ||
*end == '\f' || *end == '\r' || *end == ' ')
end++;
if (*end)
return errno = EINVAL;
if (dst)
*dst = val;
return 0;
}
int main(int argc, char *argv[])
{
char buffer[65536];
struct sockaddr_un conn;
socklen_t connlen;
int connfd, arg;
ssize_t n;
long val, left;
if (argc < 3 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s SOCKET_PATH [ LEN | -MS ] ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "All arguments except the first one, SOCKET_PATH, are integers.\n");
fprintf(stderr, "A positive integer causes a seqpacket of that length to be sent,\n");
fprintf(stderr, "a negative value causes a delay (magnitude in milliseconds).\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Fill buffer with some data. Anything works. */
{
size_t i = sizeof buffer;
while (i-->0)
buffer[i] = (i*i) ^ i;
}
connfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (connfd == -1) {
fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
connlen = set_unix_path(argv[1], &conn);
if (connect(connfd, (const struct sockaddr *)&conn, connlen) == -1) {
fprintf(stderr, "Cannot connect to %s: %s.\n", argv[1], strerror(errno));
close(connfd);
return EXIT_FAILURE;
}
/* To avoid output affecting the timing, fully buffer stdout. */
setvbuf(stdout, NULL, _IOFBF, 65536);
for (arg = 2; arg < argc; arg++)
if (parse_long(argv[arg], &val)) {
fprintf(stderr, "%s: Not an integer.\n", argv[arg]);
close(connfd);
return EXIT_FAILURE;
} else
if (val > (long)sizeof buffer) {
fprintf(stderr, "%s: Seqpacket size too large. Current limit is %zu.\n", argv[arg], sizeof buffer);
close(connfd);
return EXIT_FAILURE;
} else
if (val >= 0) {
n = send(connfd, buffer, (size_t)val, 0);
if (n == (ssize_t)val)
printf("Sent %ld-byte seqpacket successfully.\n", val);
else
if (n != (ssize_t)val && n >= 0)
fprintf(stderr, "Sent %zd bytes of a %ld-byte seqpacket.\n", n, val);
else
if (n < -1) {
fprintf(stderr, "C library bug: send() returned %zd.\n", n);
close(connfd);
return EXIT_FAILURE;
} else
if (n == -1) {
fprintf(stderr, "Send failed: %s.\n", strerror(errno));
close(connfd);
return EXIT_FAILURE;
}
} else {
left = sleep_ms(-val);
if (left)
fprintf(stderr, "Slept %ld milliseconds (out of %ld ms).\n", -val-left, -val);
else
printf("Slept %ld milliseconds.\n", -val);
}
if (close(connfd) == -1) {
fprintf(stderr, "Error closing connection: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("All done, connection closed.\n");
fflush(stdout);
return EXIT_SUCCESS;
}
Compile this using e.g. gcc -Wall -O2 send.c -o send .
For testing, I recommend you use two terminal windows. Run send in one, and receive in the other. For simplicity, I'll show the corresponding commands and outputs side-by-side. The machine this runs on is a Core i5 7200U laptop (HP EliteBook 830), running Ubuntu 16.04.4 LTS, 64-bit Linux kernel version 4.15.0-24-generic, and binaried compiled using GCC-5.4.0 20160609 (5.4.0-6ubuntu1~16.04.10) and the abovementioned commands (gcc -Wall -O2).
When we use a small delay before the final send, everything seems to work just fine:
$ ./send '\0example' 1 0 3 0 0 -1 6 │ $ ./receive '\0example'
│ Connected, peer address size is 2.
Sent 1-byte seqpacket successfully. │ Received 1 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully. │ Received 3 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Slept 1 milliseconds. │
Sent 6-byte seqpacket successfully. │ Received 6 bytes.
All done, connection closed. │ Disconnected (revents = 16).
However, when the sender sends the final few seqpackets (starting with a zero-length one) without any delays in between, I observe this:
$ ./send '\0example' 1 0 3 0 0 6 │ ./receive '\0example'
│ Connected, peer address size is 2.
Sent 1-byte seqpacket successfully. │ Received 1 bytes.
Sent 0-byte seqpacket successfully. │ Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully. │ Received 3 bytes.
Sent 0-byte seqpacket successfully. │
Sent 0-byte seqpacket successfully. │
Sent 6-byte seqpacket successfully. │
All done, connection closed. │ Disconnected (revents = 16).
See how the two zero-byte seqpackets and the 6-byte seqpacket are missed (because poll() returned revents == POLLHUP. (POLLHUP == 0x0010 == 16, so there were no other flags set either time.)
I am personally not sure if this is a bug or not. In my opinion, it is just and indication that using zero-length seqpackets is problematic, and should be avoided.
(The peer address length is 2 above, because the sender does not bind to any address, and therefore uses an unnamed Unix domain socket address (as described in the man unix man page). I don't think it is important, but I left it in just in case.)
There was a discussion on one possible solution to the problem at hand, via MSG_EOR (since recvmsg() should add MSG_EOR to the msg_flags field in the msghdr structure; and since it "should be set for all seqpackets", even zero-length ones, it would be a reliable way to detect zero-length seqpackets from end-of-input/read-side shutdown/disconnect) in the Linux Kernel Mailing List (and linux-netdev list) in May 2007. (The archived thread at Marc.info is here)
However, in Linux Unix domain seqpacket sockets, the MSG_EOR is not set nor passed, according to the initial poster, Sam Kumar. The discussion did not lead anywhere; as I read it, nobody was sure what the expected behaviour even should be.
Looking at the Linux kernel changelog for Unix domain sockets, there have been no related changes since that thread either (as of 23 July 2018).
The above programs were written in one sitting, without review; so, they could easily have bugs or thinkos in them. If you notice any, or obtain very different results (but do note timing-based effects are sometimes hard to replicate), do let me know in a comment, so I can check, and fix if necessary.
Related
The following code (in the end) represents thread function which takes in ls command from remote client and send current working directory to that client.
It successfully sends but there is one issue:
When it stops sending completely, I want it to start listening again. At line:
printf("Enter 1 if you want to exit or 0 if you don't: ");
fgets(exit_status,MAX_SIZE,stdin);
It gets stuck and it is terminated (and starts another thread) when I press Enter
Which I don't understand why? and while I was debugging I saw above print statement executes after pressing Enter despite the debugger being at the end of function (means it passed this print statement).
I want it to start listening again automatically when it finish sending data.
If anyone wants to look at my full code here is the link:https://pastebin.com/9UmTkPge
void *server_socket_ls(void *arg) {
int* exit_status = (int*)malloc(sizeof(int));
*exit_status = 0;
while (*exit_status == 0) {
//socket code is here
//code for ls
char buffer[BUFFSIZE];
int received = -1;
char data[MAX];
memset(data,0,MAX);
// this will make server wait for another command to run until it receives exit
data[0] = '\0';
if((received = recv(new_socket, buffer,BUFFSIZE,0))<0){
perror("Failed");
}
buffer[received] = '\0';
strcat (data, buffer);
if (strcmp(data, "exit")==0) // this will force the code to exit
exit(0);
puts (data);
char *args[100];
setup(data,args,0);
int pipefd[2],lenght;
if(pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
char path[MAX];
if(pid==0)
{
close(1); // close the original stdout
dup2(pipefd[1],1); // duplicate pipfd[1] to stdout
close(pipefd[0]); // close the readonly side of the pipe
close(pipefd[1]); // close the original write side of the pipe
execvp(args[0],args); // finally execute the command
}
else
if(pid>0)
{
close(pipefd[1]);
memset(path,0,MAX);
while(lenght=read(pipefd[0],path,MAX-1)){
printf("Data read so far %s\n", path);
if(send(new_socket,path,strlen(path),0) != strlen(path) ){
perror("Failed");
}
//fflush(NULL);
printf("Data sent so far %s\n", path);
memset(path,0,MAX);
}
close(pipefd[0]);
//removed so server will not terminate
}
else
{
printf("Error !\n");
exit(0);
}
printf("Enter 1 if you want to exit or 0 if you don't: ");
fgets(exit_status,MAX_SIZE,stdin);
}
}
There are many bugs:
In terminal_thread, input_command is allocated on each loop iteration -- a memory leak
Code to strip newline is broken
With .l, not specifying an IP address causes a segfault because token is NULL
The port number in terminal_thread for .l is 5126 which does not match the 9191 in the corresponding server code
After connecting, server_socket_file does not do anything.
In server_socket_ls, it loops on socket, bind, listen, and accept. The loop should start after the listen (i.e. only do accept in the loop and reuse the listening socket).
Other bugs marked in the code
I had to refactor the code and add some debug. It is annotated with the bugs. I use cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Here is the code. I got minimal .l (remote ls) working:
Edit: Because of the update below running over SO space limits, I've elided the first code block I posted here.
Here is the debug.txt output:
term term: PROMPT
term term: FGETS
ls ls: ENTER
ls ls: SOCKET
file file: ENTER
ls ls: BIND prtNum=9191
file file: BIND portNum=6123
ls ls: LISTEN
term term: COMMAND '.l'
term term: port=9191
ls ls: ACCEPTED
term term: PROMPT
This program is exiting as soon as its stops sending data at exit(0) and so doesn't ask for exit_status. Is there a way somehow to make it not stop and instead the terminal prompt reappears along with servers listening at the back? –
Dragut
Because I sensed the urgency, I erred on the side of a partial solution now is better than a perfect solution too late.
I may have introduced a bug with an extraneous exit call in the ls server parent process (now fixed).
But, there are other issues ...
The main issue is that the server (for ls) is prompting the user whether to continue or not (on stdout/stdin). This doesn't work too well.
It's the client (i.e. terminal_thread) that should prompt the user. Or, as I've done it, the client will see exit at the command prompt, then send a packet with "exit" in it to the server, and terminate. Then, the server will see this command and terminate.
I refactored as much as I could without completely redoing everything.
I split off some code into functions. Some of the can/could be reused to implement the "file" server.
But, I'd put both functions into a single server thread. I'd have the server look at the "command" it gets and do either of the actions based on the command. Since there's no code for actually doing something in the "file" server [yet] it's difficult to rework.
One thing to fix [which I did not have time for]:
The .l command is of the form: .l [ip_address]. The default for ip_address is 127.0.0.1. But, this should be split into two commands (e.g.):
attach [ip_address]
ls [ls arguments]
Anyway, here's the updated code. I had to move a bit quickly, so it's not quite as clean as I'd like.
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#if 1
#include <time.h>
#endif
#define BACKLOG 10
#define MAX_SIZE 200
#define BACKLOG 10
#define BUFFSIZE 2048
#define MAXPENDING 5
#define MAX 2048
__thread char *tid;
__thread char dbgstrbuf[1000];
FILE *xfdbg;
double tsczero = 0.0;
typedef struct server_arg {
int portNum;
} server_arg;
typedef struct server_arg1 {
int portNum;
} server_arg1;
double
tscgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_MONOTONIC,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tsczero;
return sec;
}
void
dbgprt(const char *fmt,...)
{
va_list ap;
char msg[1000];
char *bp = msg;
bp += sprintf(bp,"[%.9f/%4s] ",tscgetf(),tid);
va_start(ap,fmt);
bp += vsprintf(bp,fmt,ap);
va_end(ap);
fputs(msg,xfdbg);
}
const char *
dbgstr(const char *str,int len)
{
char *bp = dbgstrbuf;
if (len < 0)
len = strlen(str);
bp += sprintf(bp,"'");
for (int i = 0; i < len; ++i) {
int chr = str[i];
if ((chr > 0x20) && (chr <= 0x7E))
bp += sprintf(bp,"%c",chr);
else
bp += sprintf(bp,"{%2.2X}",chr);
}
bp += sprintf(bp,"'");
return dbgstrbuf;
}
void
setup(char inputBuffer[], char *args[], int *background)
{
const char s[4] = " \t\n";
char *token;
token = strtok(inputBuffer, s);
int i = 0;
while (token != NULL) {
args[i] = token;
i++;
// printf("%s\n", token);
token = strtok(NULL, s);
}
args[i] = NULL;
}
int
open_remote(const char *ip,unsigned short port)
{
int sock;
struct sockaddr_in echoserver;
dbgprt("open_remote: ENTER ip=%s port=%u\n",dbgstr(ip,-1),port);
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("Failed to create socket");
exit(1);
}
int enable = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(int)) < 0) {
perror("error");
}
memset(&echoserver, 0, sizeof(echoserver));
echoserver.sin_family = AF_INET;
echoserver.sin_addr.s_addr = inet_addr(ip);
// NOTE/BUG: this port number does _not_ match any server port
#if 0
echoserver.sin_port = htons(5126);
#else
dbgprt("term: port=%u\n",port);
echoserver.sin_port = htons(port);
#endif
if (connect(sock, (struct sockaddr *) &echoserver,
sizeof(echoserver)) < 0) {
perror("Failed to connect with server");
exit(1);
}
dbgprt("open_remote: EXIT sock=%d\n",sock);
return sock;
}
void *
terminal_thread(void *arg)
{
// NOTE/FIX: do this _once_
#if 1
char *input_command = malloc(MAX_SIZE);
#endif
tid = "term";
char buffer[BUFFSIZE];
int sock_ls = -1;
while (1) {
dbgprt("term: PROMPT\n");
printf(">> ");
//memset(input_command,0,strlen(str));
// NOTE/BUG: this is a memory leak
#if 0
char *input_command = malloc(MAX_SIZE);
#endif
dbgprt("term: FGETS\n");
fgets(input_command, MAX_SIZE, stdin);
// NOTE/BUG: code is broken to strip newline
#if 0
if ((strlen(input_command) > 0) &&
(input_command[strlen(input_command) - 1] == '\n'))
input_command[strlen(input_command) - 1] = '\0';
#else
input_command[strcspn(input_command,"\n")] = 0;
#endif
dbgprt("term: COMMAND %s\n",dbgstr(input_command,-1));
char list[] = "ls";
char cp[] = "cp";
#if 0
char s[100];
printf("%s\n", getcwd(s,100));
chdir("Desktop");
printf("%s\n", getcwd(s,100));
#endif
// exit program (and exit server)
if (strcmp(input_command,"exit") == 0) {
if (sock_ls >= 0) {
dbgprt("term: SENDEXIT\n");
if (send(sock_ls,"exit",4,0) < 0) {
perror("send/exit");
exit(1);
}
break;
}
}
if (strcmp(input_command, list) == 0) {
// ls code will run here
}
if ((input_command[0] == '.') && (input_command[1] == 'l')) {
printf("remote ls\n");
char ip[20];
const char c[2] = " ";
// strcpy(str,input_command);
char *token;
// get the first token
token = strtok(input_command, c);
// walk through other tokens
int i = 0;
while (token != NULL && i != -1) {
token = strtok(NULL, c);
i--;
}
#if 1
if (token == NULL) {
token = "127.0.0.1";
printf("no IP address found -- using %s\n",token);
}
#endif
if (sock_ls < 0)
sock_ls = open_remote(token,9191);
char s[100];
strcpy(s, "ls");
// NOTE/BUG: this blows away the "s" in "ls" because s is _set_ with strcpy
#if 0
s[strlen(s) - 1] = '\0'; // fgets doesn't automatically discard '\n'
#endif
unsigned int echolen;
echolen = strlen(s);
int received = 0;
/* send() from client; */
if (send(sock_ls, s, echolen, 0) != echolen) {
perror("Mismatch in number of sent bytes");
}
fprintf(stdout, "Message from server: ");
int bytes = 0;
/* recv() from server; */
if ((bytes = recv(sock_ls, buffer, echolen, 0)) < 1) {
perror("Failed to receive bytes from server");
}
received += bytes;
buffer[bytes] = '\0';
/* Assure null terminated string */
fprintf(stdout, buffer);
bytes = 0;
// this d {...} while block will receive the buffer sent by server
do {
buffer[bytes] = '\0';
printf("%s\n", buffer);
} while ((bytes = recv(sock_ls, buffer, BUFFSIZE - 1, 0)) >= BUFFSIZE - 1);
buffer[bytes] = '\0';
printf("%s\n", buffer);
printf("\n");
continue;
}
}
dbgprt("term: EXIT\n");
return (void *) 0;
}
int
ls_loop(int new_socket)
{
dbgprt("ls_loop: ENTER new_socket=%d\n",new_socket);
//code for ls
char buffer[BUFFSIZE];
int received = -1;
char data[MAX];
int stop = 0;
while (1) {
memset(data, 0, MAX);
// this will make server wait for another command to run until it
// receives exit
data[0] = '\0';
if ((received = recv(new_socket, buffer, BUFFSIZE, 0)) < 0) {
perror("Failed");
}
buffer[received] = '\0';
strcpy(data, buffer);
dbgprt("ls_loop: COMMAND %s\n",dbgstr(data,-1));
// this will force the code to exit
#if 0
if (strcmp(data, "exit") == 0)
exit(0);
puts(data);
#else
if (strncmp(data, "exit", 4) == 0) {
dbgprt("ls_loop: EXIT/COMMAND\n");
stop = 1;
break;
}
#endif
char *args[100];
setup(data, args, 0);
int pipefd[2], length;
if (pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
char path[MAX];
if (pid == 0) {
// NOTE/BUG: no need to close before dup2
#if 0
close(1); // close the original stdout
#endif
dup2(pipefd[1], 1); // duplicate pipfd[1] to stdout
close(pipefd[0]); // close the readonly side of the pipe
close(pipefd[1]); // close the original write side of the pipe
execvp(args[0], args); // finally execute the command
exit(1);
}
if (pid < 0) {
perror("fork");
exit(1);
}
dbgprt("ls_loop: PARENT\n");
close(pipefd[1]);
while (length = read(pipefd[0], path, MAX - 1)) {
dbgprt("ls_loop: DATAREAD %s\n",dbgstr(path,length));
if (send(new_socket, path, length, 0) != length) {
perror("Failed");
}
memset(path, 0, MAX);
}
close(pipefd[0]);
}
dbgprt("ls_loop: EXIT stop=%d\n",stop);
}
void *
server_socket_ls(void *arg)
{
tid = "ls";
dbgprt("lsmain: ENTER\n");
do {
server_arg *s = (server_arg *) arg;
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
dbgprt("lsmain: SOCKET\n");
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(int)) < 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(s->portNum);
dbgprt("lsmain: BIND prtNum=%u\n",s->portNum);
if (bind(server_fd, (struct sockaddr *) &address, sizeof(address))
< 0) {
perror("bind failed");
}
dbgprt("lsmain: LISTEN\n");
if (listen(server_fd, 3) < 0) {
perror("listen");
}
while (1) {
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t *) & addrlen)) < 0) {
perror("accept");
}
dbgprt("lsmain: ACCEPTED\n");
int stop = ls_loop(new_socket);
close(new_socket);
if (stop) {
dbgprt("lsmain: STOP\n");
break;
}
}
} while (0);
dbgprt("lsmain: EXIT\n");
return (void *) 0;
}
void *
server_socket_file(void *arg)
{
tid = "file";
dbgprt("file: ENTER\n");
server_arg1 *s1 = (server_arg1 *) arg;
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int))
< 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(s1->portNum);
dbgprt("file: BIND portNum=%u\n",s1->portNum);
if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) {
perror("bind failed");
}
if (listen(server_fd, 3) < 0) {
perror("listen");
}
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t *) & addrlen)) < 0) {
perror("accept");
}
printf("Server Connected\n");
}
int
main(int argc, char const *argv[])
{
tid = "main";
tsczero = tscgetf();
server_arg *s = (server_arg *) malloc(sizeof(server_arg));
server_arg1 *s1 = (server_arg1 *) malloc(sizeof(server_arg1));
pthread_t id_1;
pthread_t id_2;
pthread_t id_3;
xfdbg = fopen("debug.txt","w");
setlinebuf(xfdbg);
if (pthread_create(&id_3, NULL, terminal_thread, NULL) != 0) {
perror("pthread_create");
}
// NOTE/BUG: this port (or the one below) doesn't match the client code
// port of 5126
s->portNum = 9191;
pthread_create(&id_1, NULL, server_socket_ls, s);
s1->portNum = 6123;
if (0)
pthread_create(&id_2, NULL, server_socket_file, s1);
pthread_join(id_1, NULL);
if (0)
pthread_join(id_2, NULL);
pthread_join(id_3, NULL);
// NOTE/BUG: pthread_exit in main thread is wrong
#if 0
pthread_exit(0);
#else
fclose(xfdbg);
return 0;
#endif
}
UPDATE:
Feedback 2: the program does make terminal thread to reappear, but it doesn't listen anymore. When I tried to send ls command again from remote pc, it just blocks (and debugging shows it is because it gets stuck at blocking receive function). –
Dragut
I tried to avoid too much refactoring, but now, I've added more changes. This version is almost a complete rearchitecting:
pthread_create is okay when testing, but isn't general enough if the server is on a different system.
Usually, the client and server are separate programs (e.g. we start the server in a different window or from systemd).
The server usually creates a subprocess/subthread to transfer the request (Below, I've done a fork but the server could do pthread_create).
This child process handles everything after the accept, so the server main process is free to loop on accept and have multiple simultaneous clients.
Because we're using stream sockets (e.g. TCP), each side needs to know when to stop reading. The usual is to create a struct that is a descriptor of the data to follow (e.g. xmsg_t below) that has a "type" and a "payload length".
Every bit of payload data that is sent/received is prefixed by such a descriptor.
In other words, we need a simple "protocol"
Now, we need two windows (they can be on different systems):
To start server: ./myprogram -s
To start client: ./myprogram
Here's the refactored code. It is annotated:
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#if 1
#include <errno.h>
#include <time.h>
#include <sys/file.h>
#include <sys/wait.h>
#endif
#define MAXBUFF 2048 // max buffer size
#define MAXPENDING 5 // max number of connections (listen)
#define MAXARG 100 // max number of args
#define PORTNO 9191 // default port number
#if 0
#define STOP_SIGNO SIGTERM // stop signal to use
#else
#define STOP_SIGNO SIGHUP // stop signal to use
#endif
#define CLOSEME(_fd) \
do { \
dbgprt("CLOSEME fd=%d (" #_fd ")\n",_fd); \
if (_fd >= 0) \
close(_fd); \
_fd = -1; \
} while (0)
int opt_h; // 1=send HELO message
int opt_s; // 1=doserver, 0=doclient
int opt_n; // 1=run server command in foreground
char ipaddr[100] = { "127.0.0.1" };
unsigned short portno = PORTNO;
pid_t server_pid; // pid of server main process
volatile int server_signo; // signal received by server main
__thread char *tid;
__thread char dbgstrbuf[MAXBUFF + 1];
int dbgfd = -1;
double tsczero = 0.0;
typedef struct {
int xmsg_type;
int xmsg_paylen;
} xmsg_t;
enum {
XMSG_NOP,
XMSG_CMD,
XMSG_DATA,
XMSG_EOF,
};
double
tscgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_MONOTONIC,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tsczero;
return sec;
}
#if _USE_ZPRT_
#ifndef DEBUG
#define DEBUG 1
#endif
#endif
#if DEBUG
#define dbgprt(_fmt...) \
xdbgprt(__FUNCTION__,_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
void
xdbgprt(const char *fnc,const char *fmt,...)
{
va_list ap;
char msg[MAXBUFF * 4];
char *bp = msg;
int sverr = errno;
bp += sprintf(bp,"[%.9f/%4s] %s: ",tscgetf(),tid,fnc);
va_start(ap,fmt);
bp += vsprintf(bp,fmt,ap);
va_end(ap);
// when doing forks, we have to lock the stream to guarantee atomic,
// non-interspersed messages that are sequential
flock(dbgfd,LOCK_EX);
lseek(dbgfd,0,2);
ssize_t remlen = bp - msg;
ssize_t curlen;
for (bp = msg; remlen > 0; remlen -= curlen, bp += curlen) {
curlen = write(dbgfd,bp,remlen);
if (curlen < 0) {
perror("xdbgprt");
break;
}
}
flock(dbgfd,LOCK_UN);
errno = sverr;
}
const char *
dbgstr(const char *str,int len)
{
char *bp = dbgstrbuf;
if (len < 0)
len = strlen(str);
bp += sprintf(bp,"'");
for (int i = 0; i < len; ++i) {
int chr = str[i];
if ((chr > 0x20) && (chr <= 0x7E))
bp += sprintf(bp,"%c",chr);
else
bp += sprintf(bp,"{%2.2X}",chr);
}
bp += sprintf(bp,"'");
return dbgstrbuf;
}
// tokenize -- convert buffer to tokens
int
tokenize(char **argv,const char *cmdbuf)
{
static char tokbuf[MAXBUFF];
char **av = argv;
strcpy(tokbuf,cmdbuf);
char *token = strtok(tokbuf," ");
while (token != NULL) {
*av++ = token;
token = strtok(NULL," ");
}
*av = NULL;
return (av - argv);
}
// xsend -- send buffer (guaranteed delivery)
ssize_t
xsend(int sock,const void *vp,size_t buflen,int flags)
{
const char *buf = vp;
ssize_t curlen;
ssize_t totlen = 0;
dbgprt("ENTER buflen=%zu flags=%8.8X\n",buflen,flags);
for (; totlen < buflen; totlen += curlen) {
dbgprt("LOOP totlen=%zd\n",totlen);
curlen = send(sock,&buf[totlen],buflen - totlen,flags);
if (curlen <= 0)
break;
}
dbgprt("EXIT totlen=%zd\n",totlen);
return totlen;
}
// xrecv -- receive buffer (guaranteed delivery)
ssize_t
xrecv(int sock,void *vp,size_t buflen,int flags)
{
char *buf = vp;
ssize_t curlen;
ssize_t totlen = 0;
dbgprt("ENTER buflen=%zu flags=%8.8X\n",buflen,flags);
for (; totlen < buflen; totlen += curlen) {
dbgprt("LOOP totlen=%zu\n",totlen);
curlen = recv(sock,&buf[totlen],buflen - totlen,flags);
if (curlen <= 0)
break;
}
dbgprt("EXIT totlen=%zd\n",totlen);
return totlen;
}
// open_remote -- client open connection to server
int
open_remote(const char *ip,unsigned short port)
{
int sock;
struct sockaddr_in echoserver;
dbgprt("ENTER ip=%s port=%u\n",dbgstr(ip,-1),port);
if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) {
perror("Failed to create socket");
exit(1);
}
// NOTE/BUG: only server (who does bind) needs to do this
#if 0
int enable = 1;
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(enable)) < 0) {
perror("error");
}
#endif
memset(&echoserver,0,sizeof(echoserver));
echoserver.sin_family = AF_INET;
echoserver.sin_addr.s_addr = inet_addr(ip);
echoserver.sin_port = htons(port);
if (connect(sock,(struct sockaddr *) &echoserver,sizeof(echoserver)) < 0) {
perror("Failed to connect with server");
exit(1);
}
dbgprt("EXIT sock=%d\n",sock);
return sock;
}
// send_cmd -- client send command to server and process reply
void
send_cmd(int type,const char *cmd,int paylen)
{
int sock;
xmsg_t xmsg;
char buffer[MAXBUFF];
dbgprt("ENTER type=%d\n",type);
// open socket to remote server
sock = open_remote(ipaddr,portno);
// send command descriptor
xmsg.xmsg_type = type;
if (paylen < 0)
paylen = strlen(cmd);
xmsg.xmsg_paylen = paylen;
xsend(sock,&xmsg,sizeof(xmsg),0);
// send command payload
xsend(sock,cmd,xmsg.xmsg_paylen,0);
fprintf(stdout,"Message from server:\n");
int received = 0;
int bytes;
// get all data that the server sends back
while (1) {
dbgprt("LOOP\n");
// get descriptor for next chunk
xrecv(sock,&xmsg,sizeof(xmsg),0);
// handle EOF from server
if (xmsg.xmsg_paylen <= 0)
break;
// get payload
bytes = recv(sock,buffer,xmsg.xmsg_paylen,0);
dbgprt("RCVD bytes=%d\n",bytes);
#if 0
if (bytes == 0)
break;
#endif
/* recv() from server; */
if (bytes < 0) {
perror("Failed to receive bytes from server");
break;
}
received += bytes;
dbgprt("PAYLOAD %s\n",dbgstr(buffer,bytes));
// send payload to terminal
fwrite(buffer,1,bytes,stdout);
}
close(sock);
dbgprt("EXIT\n");
}
void
doclient(void)
{
char cmdbuf[MAXBUFF];
char *argv[MAXARG];
tid = "clnt";
while (1) {
dbgprt("PROMPT\n");
printf(">> ");
fflush(stdout);
dbgprt("FGETS\n");
fgets(cmdbuf,sizeof(cmdbuf),stdin);
cmdbuf[strcspn(cmdbuf,"\n")] = 0;
dbgprt("COMMAND %s\n",dbgstr(cmdbuf,-1));
// tokenize the line
int argc = tokenize(argv,cmdbuf);
if (argc <= 0)
continue;
// set/display remote server IP address
if (strcmp(argv[0],"remote") == 0) {
if (argc >= 2)
strcpy(ipaddr,argv[1]);
if (ipaddr[0] != 0)
printf("REMOTE: %s\n",ipaddr);
continue;
}
// stop server
if (strcmp(argv[0],"stop") == 0) {
if (ipaddr[0] != 0) {
dbgprt("STOP/SERVER\n");
send_cmd(XMSG_CMD,cmdbuf,-1);
}
ipaddr[0] = 0;
continue;
}
// exit client program
if (strcmp(argv[0],"exit") == 0) {
dbgprt("STOP/CLIENT\n");
break;
}
// send command and echo response to terminal
send_cmd(XMSG_CMD,cmdbuf,-1);
}
dbgprt("EXIT\n");
}
// server_cmd -- process command on server
void
server_cmd(int new_socket)
{
xmsg_t xmsg;
char cmdbuf[MAXBUFF];
char *argv[MAXARG];
dbgprt("ENTER new_socket=%d\n",new_socket);
do {
// get command descriptor
xrecv(new_socket,&xmsg,sizeof(xmsg),0);
// get command text
xrecv(new_socket,cmdbuf,xmsg.xmsg_paylen,0);
cmdbuf[xmsg.xmsg_paylen] = 0;
dbgprt("COMMAND %s\n",dbgstr(cmdbuf,-1));
// tokenize the command
int argc = tokenize(argv,cmdbuf);
if (argc <= 0)
break;
// stop the server
if (strcmp(argv[0],"stop") == 0) {
dbgprt("KILL server_pid=%d\n",server_pid);
// FIXME -- we could send a "stopping server" message here
// send EOF to client
xmsg.xmsg_type = XMSG_EOF;
xmsg.xmsg_paylen = 0;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
// signal the server main process to stop (cleanly)
if (opt_s)
server_signo = STOP_SIGNO;
else
kill(server_pid,STOP_SIGNO);
break;
}
int pipefd[2];
int length;
if (pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
dbgprt("FORK pid=%d\n",pid);
// invoke the target program (under a pipe)
if (pid == 0) {
tid = "exec";
dbgprt("DUP2\n");
fflush(stdout);
int err = dup2(pipefd[1],1); // duplicate pipefd[1] to stdout
if (err < 0)
perror("dup2");
CLOSEME(pipefd[0]); // close the readonly side of the pipe
CLOSEME(pipefd[1]); // close the write side of the pipe
dbgprt("EXECVP\n");
CLOSEME(dbgfd);
if (opt_h) {
int len = sprintf(cmdbuf,"HELO\n");
write(1,cmdbuf,len);
}
execvp(argv[0],argv); // finally execute the command
perror("execvp");
exit(1);
}
// fork error
if (pid < 0) {
perror("fork");
exit(1);
}
dbgprt("PARENT\n");
CLOSEME(pipefd[1]);
// grab all output from the target program and send in packets to
// client
while (1) {
dbgprt("READBEG\n");
length = read(pipefd[0],cmdbuf,sizeof(cmdbuf));
dbgprt("READEND length=%d\n",length);
if (length < 0) {
perror("readpipe");
break;
}
if (length == 0)
break;
dbgprt("READBUF %s\n",dbgstr(cmdbuf,length));
// send descriptor for this chunk
xmsg.xmsg_type = XMSG_DATA;
xmsg.xmsg_paylen = length;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
// send the payload
if (xsend(new_socket,cmdbuf,length,0) != length) {
perror("Failed");
}
}
CLOSEME(pipefd[0]);
// tell client we have no more data
xmsg.xmsg_paylen = 0;
xmsg.xmsg_type = XMSG_EOF;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
} while (0);
CLOSEME(new_socket);
dbgprt("EXIT\n");
}
void
sighdr(int signo)
{
server_signo = signo;
}
void
doserver(void)
{
int server_fd,new_socket;
struct sockaddr_in address;
pid_t pid;
tid = "serv";
dbgprt("ENTER\n");
server_pid = getpid();
#if 0
signal(STOP_SIGNO,(void *) sighdr);
#else
struct sigaction act;
sigaction(STOP_SIGNO,NULL,&act);
act.sa_sigaction = (void *) sighdr;
sigaction(STOP_SIGNO,&act,NULL);
sigset_t set;
sigemptyset(&set);
sigaddset(&set,STOP_SIGNO);
sigprocmask(SIG_UNBLOCK,&set,NULL);
#endif
#if 0
int addrlen = sizeof(address);
#else
socklen_t addrlen = sizeof(address);
#endif
dbgprt("SOCKET\n");
// Creating socket file descriptor
if ((server_fd = socket(AF_INET,SOCK_STREAM,0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(int)) < 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(portno);
dbgprt("BIND portno=%u\n",portno);
if (bind(server_fd,(struct sockaddr *) &address,sizeof(address)) < 0) {
perror("bind failed");
}
dbgprt("LISTEN\n");
if (listen(server_fd,MAXPENDING) < 0) {
perror("listen");
}
int pending = 0;
int status;
while (1) {
dbgprt("LOOP\n");
// reap all finished children
while (1) {
pid = waitpid(-1,&status,WNOHANG);
if (pid <= 0)
break;
dbgprt("REAP pid=%d pending=%d\n",pid,pending);
--pending;
}
// one of the children was given a stop command and it signaled us
if (server_signo) {
dbgprt("SIGNO server_signo=%d\n",server_signo);
break;
}
// wait for new connection from a client
// FIXME -- sending us a signal to stop cleanly is _broken_ because
// we do _not_ get an early return here (e.g. EINTR) -- we may need
// select with timeout
dbgprt("WAITACCEPT\n");
new_socket = accept(server_fd,(struct sockaddr *) &address,
(socklen_t *) &addrlen);
// stop cleanly
if (server_signo) {
dbgprt("SIGNO server_signo=%d\n",server_signo);
break;
}
if (new_socket < 0) {
if (errno == EINTR)
break;
perror("accept");
}
dbgprt("ACCEPTED\n");
// do command execution in main process (i.e. debug)
if (opt_n) {
server_cmd(new_socket);
continue;
}
pid = fork();
if (pid < 0) {
CLOSEME(new_socket);
continue;
}
// process the command in the child
if (pid == 0) {
server_cmd(new_socket);
exit(0);
}
++pending;
dbgprt("CHILD pid=%d\n",pid);
// server main doesn't need this after fork
#if 1
CLOSEME(new_socket);
#endif
}
// reap all children
while (pending > 0) {
pid = waitpid(-1,&status,0);
if (pid <= 0)
break;
dbgprt("REAP pid=%d pending=%d\n",pid,pending);
--pending;
}
dbgprt("EXIT\n");
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'h':
opt_h = ! opt_h;
break;
case 'n': // do _not_ fork server
opt_n = ! opt_n;
break;
case 'p':
portno = (*cp != 0) ? atoi(cp) : PORTNO;
break;
case 's': // invoke server
opt_s = ! opt_s;
break;
}
}
tsczero = tscgetf();
#if DEBUG
int flags = O_WRONLY | O_APPEND;
if (opt_s)
flags |= O_TRUNC | O_CREAT;
dbgfd = open("debug.txt",flags,0644);
if (dbgfd < 0) {
perror("debug.txt");
exit(1);
}
#endif
if (opt_s)
doserver();
else
doclient();
#if DEBUG
if (dbgfd >= 0)
close(dbgfd);
#endif
return 0;
}
I am learning socket programming in C language, and this is an incomprehensible problem I encountered during my study.
Today I am trying to send a HTTP request to my test server which host an Apache example website, then receive the response from test server. Here is a part of my receive code.
unsigned long recv_size = 0;
unsigned long response_size = 4096;
int ret = 0;
char *recv_buff = (char *)malloc(response_size);
while (1)
{
// ret = recv(socket, recv_buff, response_size, MSG_WAITALL); // cannot get all data
ret = read(socket, recv_buff, response_size); // same effect as the above
recv_size += ret;
if (ret < 0)
error(strerror(errno));
else if (ret == 0)
break; // all data recved
}
The normal result of my test with burpsuit is this.
But what I received with the C language program was incomplete data.
I searched the reason for one night, but I still did not find a solution for my problem. Whether it is to set the buff to a super large size or any other method, the complete data cannot be accepted at all.
The traffic monitored from wireshark is ok, but my program still cannot receive the complete data. What is the problem?
If you know why, please let me know. THX. (o゜▽゜)o☆
UPDATE
The while loop will execute twice, and first time the value of ret is 3343, and second time is 0, so the loop will stop here.
You can get a short read on a socket.
But, your code to handle that has a few issues.
You're allocating a buffer of size response_size. You are always reading that amount instead of reducing the amount read by the amount you've already read on a prior loop iteration.
This can cause you to read past the end of the buffer causing UB (undefined behavior).
Your termination condition is if (ret == 0). This can fail if another packet arrives "early". You'll never see a ret of 0, because the partial data from the next packet will make it non-zero
Here's the corrected code:
#if 0
unsigned long recv_size = 0;
#endif
unsigned long response_size = 4096;
int ret = 0;
char *recv_buff = (char *) malloc(response_size);
#if 1
unsigned long remaining_size = response_size;
unsigned long offset = 0;
#endif
for (; remaining_size > 0; remaining_size -= ret, offset += ret) {
ret = read(socket, &recv_buff[offset], remaining_size);
if (ret < 0)
error(strerror(errno));
}
UPDATE:
The above code corrects some of the issues. But, for a variable length source [such as http], we don't know how much to read at the outset.
So, we have to parse the headers and look for the "Content-Length" field. This will tell us how much to read.
So, we'd like to have line oriented input for the headers. Or, manage our own buffer
Assuming we can parse that value, we have to wait for the empty line to denote the start of the payload. And, then we can loop on that exact amount.
Here's some code that attempts the header parsing and saving of the payload. I've coded it, but not compiled it. So, you can take it as pseudo code:
unsigned long recv_size = 0;
unsigned long response_size = 4096;
char *recv_buff = malloc(response_size + 1);
// line oriented header buffer
char *endl = NULL;
unsigned long linelen;
char linebuf[1000];
int ret = 0;
// read headers
while (1) {
// fill up a chunk of data
while (recv_size < response_size) {
recv_buff[recv_size] = 0;
// do we have a line end?
endl = strstr(recv_buff,"\r\n");
if (endl != NULL)
break;
ret = read(socket, &recv_buff[recv_size], response_size - recv_size);
if (ret < 0)
error(strerror(errno));
if (ret == 0)
break;
recv_size += ret;
}
// error -- no line end but short read
if (endl == NULL)
error(strerror(errno));
// copy header to work buffer
linelen = endl - recv_buff;
memcpy(linebuf,recv_buff,linelen);
linebuf[linelen] = 0;
// remove header from receive buffer
linelen += 2;
recv_size -= linelen;
if (recv_size > 0)
memcpy(recv_buff,&recv_buff[linelen],recv_size);
// stop on end of headers (back to back "\r\n")
if ((recv_size >= 2) && (recv_buff[0] == '\r') && (recv_buff[1] == '\n')) {
memcpy(recv_buff,&recv_buff[2],recv_size - 2);
recv_size -= 2;
break;
}
// parse line work buffer for keywords ... (e.g.)
content_length = ...;
}
// save payload to file
while (content_length > 0) {
// write out prior payload amount
if (recv_size > 0) {
write(file_fd,recv_buff,recv_size);
content_length -= recv_size;
recv_size = 0;
continue;
}
recv_size = read(socket,recv_buff,response_size);
if (recv_size < 0)
error(strerror(errno));
if (recv_size == 0)
break;
}
UPDATE #2:
Yeah, it hard to make the pseudo code run, and the returned values are all garbled
Okay, here is a soup-to-nuts working version that I've tested against my own http server.
I had to create my own routines for the parts you didn't post (e.g. connect, etc.).
At the core, there might have been a minor tweak to the buffer slide code [it was sliding by an extra 2 bytes in one place], but, otherwise it was pretty close to my previous version
// htprcv/htprcv.c -- HTTP receiver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <error.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
typedef unsigned char byte;
#define HTPSLIDE(_rmlen) \
recv_size = htpslide(recv_buff,recv_size,_rmlen)
#define _dbgprt(_fmt...) \
fprintf(stderr,_fmt)
#if DEBUG || _USE_ZPRT_
#define dbgprt(_lvl,_fmt...) \
do { \
if (dbgok(_lvl)) \
_dbgprt(_fmt); \
} while (0)
#define dbgexec(_lvl,_expr) \
do { \
if (dbgok(_lvl)) \
_expr; \
} while (0)
#else
#define dbgprt(_lvl,_fmt...) \
do { \
} while (0)
#define dbgexec(_lvl,_expr) \
do { \
} while (0)
#endif
#define dbgok(_lvl) \
opt_d[(byte) #_lvl[0]]
byte opt_d[256];
char *opt_o;
#define HEXMAX 16
// htpconn -- do connect to server
int
htpconn(const char *hostname,unsigned short portno)
{
struct addrinfo hints, *res;
struct hostent *hostent;
int ret;
char portstr[20];
int sockfd;
/* Prepare hint (socket address input). */
hostent = gethostbyname(hostname);
if (hostent == NULL)
error(1,errno,"htpconn: gethostbyname -- %s\n",hostname);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
sprintf(portstr, "%d", portno);
getaddrinfo(NULL, portstr, &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0)
error(1,errno,"htpconn: socket\n");
// do the actual connection
ret = connect(sockfd, res->ai_addr, res->ai_addrlen);
if (ret < 0)
error(1,errno,"htprcv: read header\n");
return sockfd;
}
// htpslide -- slide buffer (strip out processed data)
size_t
htpslide(char *recv_buff,size_t recv_size,int slidelen)
{
size_t new_size;
if (slidelen > recv_size)
slidelen = recv_size;
new_size = recv_size - slidelen;
dbgprt(S,"htpslide: slidelen=%d recv_size=%zu new_size=%zu\n",
slidelen,recv_size,new_size);
memcpy(&recv_buff[0],&recv_buff[slidelen],new_size);
return new_size;
}
// _htphex -- dump a line in hex
void
_htphex(unsigned long off,const void *vp,size_t xlen)
{
const byte *bp = vp;
int idx;
int chr;
char hexbuf[200];
char alfbuf[200];
char *hexptr = hexbuf;
char *alfptr = alfbuf;
for (idx = 0; idx < HEXMAX; ++idx) {
chr = bp[idx];
if ((idx % 4) == 0)
*hexptr++ = ' ';
if (idx < xlen) {
hexptr += sprintf(hexptr,"%2.2X",chr);
if ((chr < 0x20) || (chr > 0x7E))
chr = '.';
}
else {
hexptr += sprintf(hexptr," ");
chr = ' ';
}
*alfptr++ = chr;
}
*hexptr = 0;
*alfptr = 0;
_dbgprt(" %8.8lX: %s *%s*\n",off,hexbuf,alfbuf);
}
// htphex -- dump a buffer in hex
void
htphex(const char *buf,size_t buflen,const char *reason)
{
size_t off = 0;
size_t xlen;
if (reason != NULL)
_dbgprt("htphex: DUMP buf=%p buflen=%zu (from %s)\n",
buf,buflen,reason);
for (; buflen > 0; buflen -= xlen, buf += xlen, off += xlen) {
xlen = buflen;
if (xlen > HEXMAX)
xlen = HEXMAX;
_htphex(off,buf,xlen);
}
}
// htpsym -- get symbol/value
int
htpsym(char *linebuf,char *sym,char *val)
{
char *cp;
int match;
dbgprt(H,"htpsym: PARAM linebuf='%s'\n",linebuf);
// FORMAT:
// foo-bar: baz
do {
match = 0;
cp = strchr(linebuf,':');
if (cp == NULL)
break;
*cp++ = 0;
strcpy(sym,linebuf);
for (; (*cp == ' ') || (*cp == '\t'); ++cp);
strcpy(val,cp);
match = 1;
dbgprt(H,"htpsym: SYMBOL sym='%s' val='%s'\n",sym,val);
} while (0);
return match;
}
// htprcv -- receive server response
void
htprcv(int sockfd,int fdout)
{
size_t recv_size = 0;
size_t response_size = 4096;
char *recv_buff = malloc(response_size + 1);
// line oriented header buffer
char *endl = NULL;
size_t linelen;
char linebuf[1000];
ssize_t ret = 0;
off_t content_length = 0;
// read headers
while (1) {
// fill up a chunk of data
while (recv_size < response_size) {
recv_buff[recv_size] = 0;
// do we have a line end?
endl = strstr(recv_buff,"\r\n");
if (endl != NULL)
break;
// read a chunk of data
ret = read(sockfd,&recv_buff[recv_size],response_size - recv_size);
if (ret < 0)
error(1,errno,"htprcv: read header\n");
if (ret == 0)
break;
recv_size += ret;
dbgprt(R,"htprcv: READ ret=%zd\n",ret);
dbgexec(R,htphex(recv_buff,recv_size,"htprcv/READ"));
}
// error -- no line end but short read
if (endl == NULL)
error(1,0,"htprcv: no endl\n");
// copy header to work buffer
linelen = endl - recv_buff;
memcpy(linebuf,recv_buff,linelen);
linebuf[linelen] = 0;
// remove header from receive buffer
linelen += 2;
HTPSLIDE(linelen);
// stop on end of headers (back to back "\r\n")
if ((recv_size >= 2) &&
(recv_buff[0] == '\r') && (recv_buff[1] == '\n')) {
HTPSLIDE(2);
break;
}
// parse line work buffer for keywords ...
char sym[100];
char val[1000];
if (! htpsym(linebuf,sym,val))
continue;
if (strcasecmp(sym,"Content-Length") == 0) {
content_length = atoi(val);
continue;
}
}
// save payload to file
while (content_length > 0) {
// write out prior payload amount
if (recv_size > 0) {
dbgexec(W,htphex(recv_buff,recv_size,"htprcv/WRITE"));
ret = write(fdout,recv_buff,recv_size);
if (ret < 0)
error(1,errno,"htprcv: write body\n");
content_length -= recv_size;
recv_size = 0;
continue;
}
// read in new chunk of payload
ret = read(sockfd,recv_buff,response_size);
if (ret < 0)
error(1,errno,"htprcv: read body\n");
if (ret == 0)
break;
recv_size = ret;
}
free(recv_buff);
}
// htpget -- do initial dialog
void
htpget(int sockfd,const char *hostname,const char *file)
{
char *bp;
char buf[1024];
ssize_t resid;
ssize_t xlen;
size_t off;
bp = buf;
if (file == NULL)
file = "/";
bp += sprintf(bp,"GET %s HTTP/1.1\r\n",file);
if (hostname == NULL)
hostname = "localhost";
bp += sprintf(bp,"Host: %s\r\n",hostname);
if (0) {
bp += sprintf(bp,"User-Agent: %s\r\n","curl/7.61.1");
}
else {
bp += sprintf(bp,"User-Agent: %s\r\n","htprcv");
}
bp += sprintf(bp,"Accept: */*\r\n");
bp += sprintf(bp,"\r\n");
resid = bp - buf;
off = 0;
for (; resid > 0; resid -= xlen, off += xlen) {
xlen = write(sockfd,buf,resid);
if (xlen < 0)
error(1,errno,"htpget: write error\n");
}
}
// main -- main program
int
main(int argc,char **argv)
{
char *cp;
char *portstr;
unsigned short portno;
int sockfd;
int filefd;
char url[1000];
--argc;
++argv;
//setlinebuf(stdout);
setlinebuf(stderr);
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch(cp[-1]) {
case 'd': // debug options
if (*cp == 0)
cp = "SHRW";
for (; *cp != 0; ++cp)
opt_d[(byte) *cp] = 1;
break;
case 'o': // output file
opt_o = cp;
break;
}
}
// get the remote host:port
do {
if (argc <= 0) {
strcpy(url,"localhost:80");
break;
}
strcpy(url,*argv++);
--argc;
} while (0);
// get remote port number
portstr = strchr(url,':');
if (portstr != NULL)
*portstr++ = 0;
else
portstr = "80";
portno = atoi(portstr);
// open the output file (or send to stdout)
do {
if (opt_o == NULL) {
filefd = 1;
break;
}
filefd = open(opt_o,O_WRONLY | O_CREAT,0644);
if (filefd < 0)
filefd = 1;
} while (0);
// establish connection
sockfd = htpconn(url,portno);
// send the file request
htpget(sockfd,NULL,"/");
// receive the server response
htprcv(sockfd,filefd);
close(sockfd);
return 0;
}
I am new to serail programing in Linux (Fedora 12) using C Language. I have a simple device, it is supposed to get 3 bytes command in hex (wirte) like {0x02,0x03,0x0D} and return 4 bytes response.
First I wrote a simple java program on windows, and I get the correct response.. as and when,I switch to linux, I can't read from serial com port neither using java nor C language.
I tried using libraries like rs232 .. but still the problem remains.
I can open "/dev/ttyS0", and write on it .. (none of them returns any Error), but read is not possible ..
If I use canonical mode, the program blocks on reading until i kill the program.. If use non-canonical mode, with VMIN=0 and VTIME=5, read function returns whith -107725432 bytes for example ..
(I have tried reading and writing byte by byte or all at the same time .. no difference ..)
rs232.c
#include "rs232.h"
int Cport[6],
error;
struct termios new_port_settings,
old_port_settings[6];
char comports[6][16]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5"};
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
int baudr,
status;
if((comport_number>5)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 2400 : baudr = B2400;
break;
case 4800 : baudr = B4800;
break;
case 9600 : baudr = B9600;
break;
case 19200 : baudr = B19200;
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
int cbits=CS8,
cpar=0,
ipar=IGNPAR,
bstop=0;
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': cbits = CS8;
break;
case '7': cbits = CS7;
break;
case '6': cbits = CS6;
break;
case '5': cbits = CS5;
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': cpar = 0;
ipar = IGNPAR;
break;
case 'E':
case 'e': cpar = PARENB;
ipar = INPCK;
break;
case 'O':
case 'o': cpar = (PARENB | PARODD);
ipar = INPCK;
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': bstop = 0;
break;
case '2': bstop = CSTOPB;
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY);
if(Cport[comport_number]==-1)
{
perror("unable to open comport ");
return(1);
}
/* lock access so that another process can't also use the port */
if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0)
{
close(Cport[comport_number]);
perror("Another process has locked the comport.");
return(1);
}
error = tcgetattr(Cport[comport_number], old_port_settings + comport_number);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to read portsettings ");
return(1);
}
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
new_port_settings.c_iflag = ipar;
new_port_settings.c_oflag = 0;
new_port_settings.c_lflag = 0;
new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */
new_port_settings.c_cc[VTIME] = 5; /* block untill a timer expires (n * 100 mSec.) */
cfsetispeed(&new_port_settings, baudr);
cfsetospeed(&new_port_settings, baudr);
error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to adjust portsettings ");
return(1);
}
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
return(1);
}
status |= TIOCM_DTR; /* turn on DTR */
status |= TIOCM_RTS; /* turn on RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
n = read(Cport[comport_number], buf, size);
return(n);
}
int RS232_SendBuf(int comport_number, unsigned char *buf, int size)
{
return(write(Cport[comport_number], buf, size));
}
void RS232_CloseComport(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
status &= ~TIOCM_RTS; /* turn off RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
}
void RS232_flushRXTX(int comport_number)
{
tcflush(Cport[comport_number], TCIOFLUSH);
}
and
main.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rs232.h"
int main()
{
int cport_nr=0, bdrate=9600; /* 9600 baud */
char mode[]={'8','N','1',0},
str[512];
unsigned char buf[6000];
memset(buf, '\0' , 6000);
int buf_SIZE=sizeof(buf);
if(RS232_OpenComport(cport_nr, bdrate, mode))
{
printf("Can not open comport\n");
return(0);
}
unsigned char wr_buff[5];
memset(wr_buff, '\0', 5);
wr_buff[0] = 0x02;
wr_buff[1] = 0x01;
wr_buff[2] = 0x0D;
int cnt = RS232_SendBuf(cport_nr, wr_buff, 3);
printf("Number of bytes that has been written: %d\n",cnt);
if (cnt <= 0 )
return(-1);
cnt =0 ;
usleep(100000);
printf("Start Reading ... \n");
int i = 0;
do {
cnt = RS232_PollComport(cport_nr,(buf+i), 1);
i++;}
while(cnt>0);
printf ("%d bytes have been read\n");
RS232_CloseComport(cport_nr);
return (1);
}
I am really confused .. I tried almost every samples on the Internet .. An
y Idea Please?!
I have traced the program, using strace ..
...
write(3,"\2\3\r",3) = 3
fstat64(1 , {st_mode=S_IFREG| 0755, st_size =0,..}) = 0
mmap2(NULL,4096,PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS,-1,0) = 0Xb78b4000
nanosleep({0, 100000000}, NULL) = 0
read(3,"",1) = 0
.....
Can the problem be related to fedora12?
P.S. : if I haven't got response in windows, I was sure that its a problem with the device.
Solved ...
it was a mistake of me ...
In fact, every thing was ok,
the problem was in the way I print number of bytes have been read.. I had forgotten to put "cnt" in respect of %d ...
printf ("%d bytes have been read\n"); --> printf ("%d bytes have been read\n",cnt);
also i should have read only 4 bytes based on the protocol of communication with my device ..
I want to send and receive data from device to pc and vice versa. I am sending the string, but not able to receive it fully. Example: Sent string is Hello, and the output is:
Received:H
Error in read! e = -4 and received = 5
#include <string.h>
#include<stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x81
/*find these values using lsusb -v*/
uint16_t VENDOR = 0x0483;
uint16_t PRODUCT = 0x5740;
int main(void)
{
int ret = 1; //int type result
struct libusb_device **usb_dev;
struct libusb_device_descriptor desc;
struct libusb_device_handle *handle = NULL;
struct device_handle_expected;
//struct libusb_device_handle device_expected_handle = NULL;
struct libusb_device *dev, *dev_expected;
char *my_string, *my_string1;
int e = 0, config;
char found = 0;
int transferred = 0;
int received = 0;
int length = 0;
int i=0;
int count;
/*struct libusb_device *dev;
struct libusb_device **devs;
struct dev_expected;*/
// Initialize libusb
ret = libusb_init(NULL);
if(ret < 0)
{
printf("\nFailed to initialise libusb\n");
return 1;
}
else
printf("\nInit successful!\n");
// Get a list of USB devices
count = libusb_get_device_list(NULL, &usb_dev);
if (count < 0)
{
printf("\nThere are no USB devices on the bus\n");
return -1;
}
printf("\nToally we have %d devices\n", count);
while ((dev = usb_dev[i++]) != NULL)
{
ret = libusb_get_device_descriptor(dev, &desc);
if (ret < 0)
{
printf("Failed to get device descriptor\n");
libusb_free_device_list(dev, 1);
break;
}
e = libusb_open(dev, &handle);
if (e < 0)
{
printf("Error opening device\n");
libusb_free_device_list(dev, 1);
libusb_close(handle);
break;
}
if(desc.idVendor == 0x0483 && desc.idProduct == 0x5740)
{
found = 1;
break;
}
}//end of while
if(found == 0)
{
printf("\nDevice NOT found\n");
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
return 1;
}
else
{
printf("\nDevice found");
// dev_expected = dev;
//device_handle_expected = handle;
}
e = libusb_get_configuration(handle, &config);
if(e!=0)
{
printf("\n***Error in libusb_get_configuration\n");
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
return -1;
}
printf("\nConfigured value: %d", config);
if(config != 1)
{
libusb_set_configuration(handle, 1);
if(e!=0)
{
printf("Error in libusb_set_configuration\n");
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
return -1;
}
else
printf("\nDevice is in configured state!");
}
if(libusb_kernel_driver_active(handle, 0) == 1)
{
printf("\nKernel Driver Active");
if(libusb_detach_kernel_driver(handle, 0) == 0)
printf("\nKernel Driver Detached!");
else
{
printf("\nCouldn't detach kernel driver!\n");
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
return -1;
}
}
e = libusb_claim_interface(handle, 0);
if(e < 0)
{
printf("\nCannot Claim Interface");
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
return -1;
}
else
printf("\nClaimed Interface\n");
int nbytes = 64;
my_string = (char *) malloc(nbytes + 1);
my_string1 = (char *) malloc(nbytes + 1);
memset(my_string, '\0', 64);//The C library function void (an unsigned char) to the first n characters of the string pointed to, by the argument str.
memset(my_string1, '\0', 64);
strcpy(my_string, "Hello");
length = strlen(my_string);
printf("\nTo be sent: %s", my_string);
e = libusb_bulk_transfer(handle, BULK_EP_OUT, my_string, length, &transferred, 0);
if(e == 0 && transferred == length)
{
printf("\nWrite successful!");
printf("\nSent %d bytes with string: %s\n", transferred, my_string);
}
else
printf("\nError in write! e = %d and transferred = %d\n", e, transferred);
// sleep(3);
i = 0;
for(i = 0; i <= length; i++)
{
e = libusb_bulk_transfer(handle, BULK_EP_IN, my_string1,length, &received, 0); //64: Max Packet Length
if(e == 0 && received == length)
{
printf("\nReceived:");
printf("%c", my_string1[i]);
sleep(5);
}
else
{
printf("\nError in read! e = %d and received = %d bytes\n", e, received);
return -1;
}
}
libusb_release_interface(handle, 0);
libusb_free_device_list(usb_dev, 1);
libusb_close(handle);
libusb_exit(NULL);
printf("\n");
return 0;
}
Pretty certain the bulk in transfer will transfer the entire buffer at once(up to 64--or 512 if you're doing high speed--bytes), not a byte at a time.
You're iterating over the size of the expected buffer and doing a bulk in for each byte, and only printing out the first byte.
Get rid of the for loop on the read and change your printf() to be printf("%s\n",my_string1);
I have a small program that read barcodes from /dev/input/event4.
This is the code:
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <linux/input.h>
int main (int argc, char *argv[])
{
struct input_event ev;
int fd, rd;
//Open Device
if ((fd = open ("/dev/input/event4", O_RDONLY|O_NONBLOCK)) == -1){
printf ("not a vaild device.\n");
return -1;
}
while (1){
memset((void*)&ev, 0, sizeof(ev));
rd = read (fd, (void*)&ev, sizeof(ev));
if (rd <= 0){
printf ("rd: %d\n", rd);
sleep(1);
}
if(rd>0 && ev.value==0 && ev.type==1){
printf("type: %d, code: %d, value: %d, rd: %d\n", ev.type, ev.code, ev.value, rd);
}
}
return 0;
}
I have now created some barcodes with an online-generator (http://www.barcode-generator.de/V2/de/index.jsp). The barcodes are:
123456789 and 1234567890
The output of my programm when scanning the barcodes is:
type: 1, code: 2, value: 0, rd: 16
type: 1, code: 3, value: 0, rd: 16
type: 1, code: 4, value: 0, rd: 16
type: 1, code: 5, value: 0, rd: 16
type: 1, code: 6, value: 0, rd: 16
type: 1, code: 7, value: 0, rd: 16
type: 1, code: 8, value: 0, rd: 16
type: 1, code: 9, value: 0, rd: 16
type: 1, code: 10, value: 0, rd: 16
type: 1, code: 28, value: 0, rd: 16
for the 123456789
and
type: 1, code: 28, value: 0, rd: 16
for the 1234567890
So, the 10-digit-barcodes is not recognised correctly.
The code: 28 means this is an RETURN/ENTER, this is the internal terminator for a barcode, so this comes directly from the scanner.
Does anyone can tell my why ? Maybe there is something wrong with the code ?
Goodbye, Andre
You should only consider the event when the read() returns == sizeof ev, because we're reading from an input device here. If it returns zero, it means no more events are forthcoming (maybe device detached?). If it returns -1, check errno. If read() returns any other value, the kernel driver has gone bonkers and you could consider it a fatal error.
errno == EINTR is normal (occurs when a signal is delivered), it is not an error per se. It shouldn't happen here, but ignoring it (treating it as just a hiccup, not an error) is quite safe.
errno == EAGAIN occurs when you used O_NONBLOCK in the open() flags, and there is no new event available yet.
There is absolutely no reason to use O_NONBLOCK here. All it does is causes your code to waste CPU cycles, returning tens of thousands of times per second from the read() call just to return -1 with errno == EAGAIN. Just drop it, so that the read() will simply wait until a new event arrives, and returns it.
See my answer to the input_event structure description question.
In summary, for ev_type == 1 == EV_KEY:
ev_value == 0: key released (key up)
ev_value == 1: key pressed (key down)
ev_value == 2: autorepeat (key automatically repeated)
ev_code == 1 == KEY_ESC
ev_code == 2 == KEY_1
ev_code == 3 == KEY_2
ev_code == 10 == KEY_9
ev_code == 11 == KEY_0
ev_code == 28 == KEY_ENTER
The keypresses the device provided are actually 1 2 3 4 5 6 7 8 9 Enter.
(Note that you only showed the key release events; you should actually see two, one with ev_value == 1, followed by one with ev_value == 0, for each ev_code.)
The one Chinese one I've tried was very nice, although dirt cheap. It had a manual with a few barcodes, including some to switch between barcode formats (and number of digits). I vaguely remember using two barcodes to switch to another mode, and to use the minimum volume for the beeps. It seemed to retain the settings even after being detached.
Here is an example of what kind of implementation I'd use to read barcodes. I'd obviously split the barcode reading part to a separate file.
The below code is dedicated to public domain (licensed under CC0), so feel free to use it in any way you wish. There is no guarantees of any kind, so don't blame me for any breakage. (Any bug fixes are welcome; if reported, I will check and include in the below code. I recommend adding a comment below; I do read all comments to my answers every couple of days or so.)
The header file barcode.h:
#ifndef BARCODE_H
#define BARCODE_H
#include <stdlib.h>
#include <signal.h>
/* This flags turns nonzero if any signal
* installed with install_done is caught.
*/
extern volatile sig_atomic_t done;
/* Install signals that set 'done'.
*/
int install_done(const int signum);
/* Barcode device description.
* Do not meddle with the internals;
* this is here only to allow you
* to allocate one statically.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
/* Close a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_close(barcode_dev *const dev);
/* Open a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_open(barcode_dev *const dev, const char *const device_path);
/* Read a barcode, but do not spend more than maximum_ms.
* Returns the length of the barcode read.
* (although at most length-1 characters are saved at the buffer,
* the total length of the barcode is returned.)
* errno is always set; 0 if success, error code otherwise.
* If the reading timed out, errno will be set to ETIMEDOUT.
*/
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms);
#endif /* BARCODE_H */
The implementation in the following barcode.c file currently accepts only digits (0 through 1), but it should be trivial to add any other necessary keys (like KEY_A through KEY_Z). The current one ignores shift, control, et cetera, as they are not provided by any scanners as far as I know. It uses SIGRTMAX-0 realtime signal and a custom timer per barcode device to read barcodes, so you'll need to link it against librt (-lrt):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Link against the rt library; -lrt. */
#define UNUSED __attribute__((unused))
#define TIMEOUT_SIGNAL (SIGRTMAX-0)
/*
* done - flag used to exit program at SIGINT, SIGTERM etc.
*/
volatile sig_atomic_t done = 0;
static void handle_done(int signum UNUSED)
{
done = 1;
}
int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
/*
* Barcode input event device, and associated timeout timer.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
static void handle_timeout(int signum UNUSED, siginfo_t *info, void *context UNUSED)
{
if (info && info->si_code == SI_TIMER && info->si_value.sival_ptr)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info->si_value.sival_ptr, 1);
#endif
}
static int install_timeouts(void)
{
static int installed = 0;
if (!installed) {
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = handle_timeout;
act.sa_flags = SA_SIGINFO;
if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1)
return errno;
installed = 1;
}
return 0;
}
int barcode_close(barcode_dev *const dev)
{
int retval = 0;
if (!dev)
return 0;
if (dev->fd != -1)
if (close(dev->fd) == -1)
retval = errno;
dev->fd = -1;
if (dev->timer)
if (timer_delete(dev->timer) == -1)
if (!retval)
retval = errno;
dev->timer = (timer_t)0;
/* Handle all pending TIMEOUT_SIGNALs */
while (1) {
struct timespec t;
siginfo_t info;
sigset_t s;
t.tv_sec = (time_t)0;
t.tv_nsec = 0L;
sigemptyset(&s);
if (sigtimedwait(&s, &info, &t) != TIMEOUT_SIGNAL)
break;
if (info.si_code != SI_TIMER || !info.si_value.sival_ptr)
continue;
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info.si_value.sival_ptr, 1);
#endif
}
return errno = retval;
}
int barcode_open(barcode_dev *const dev, const char *const device_path)
{
struct sigevent event;
int fd;
if (!dev)
return errno = EINVAL;
dev->fd = -1;
dev->timeout = -1;
dev->timer = (timer_t)0;
if (!device_path || !*device_path)
return errno = EINVAL;
if (install_timeouts())
return errno;
do {
fd = open(device_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
errno = 0;
if (ioctl(fd, EVIOCGRAB, 1)) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EACCES;
}
dev->fd = fd;
memset(&event, 0, sizeof event);
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = TIMEOUT_SIGNAL;
event.sigev_value.sival_ptr = (void *)&(dev->timeout);
if (timer_create(CLOCK_REALTIME, &event, &dev->timer) == -1) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EMFILE;
}
return errno = 0;
}
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms)
{
struct itimerspec it;
size_t len = 0;
int status = ETIMEDOUT;
if (!dev || !buffer || length < 2 || maximum_ms < 1UL) {
errno = EINVAL;
return (size_t)0;
}
/* Initial timeout. */
it.it_value.tv_sec = maximum_ms / 1000UL;
it.it_value.tv_nsec = (maximum_ms % 1000UL) * 1000000L;
/* After elapsing, fire every 10 ms. */
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 10000000L;
if (timer_settime(dev->timer, 0, &it, NULL) == -1)
return (size_t)0;
/* Because of the repeated elapsing, it is safe to
* clear the timeout flag here. */
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 7)
__atomic_store_n((int *)&(dev->timeout), 0, __ATOMIC_SEQ_CST);
#else
__sync_fetch_and_and((int *)&(dev->timeout), 0);
#endif
while (!dev->timeout) {
struct input_event ev;
ssize_t n;
int digit;
n = read(dev->fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR)
continue;
status = errno;
break;
} else
if (n == sizeof ev) {
/* We consider only key presses and autorepeats. */
if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2))
continue;
switch (ev.code) {
case KEY_0: digit = '0'; break;
case KEY_1: digit = '1'; break;
case KEY_2: digit = '2'; break;
case KEY_3: digit = '3'; break;
case KEY_4: digit = '4'; break;
case KEY_5: digit = '5'; break;
case KEY_6: digit = '6'; break;
case KEY_7: digit = '7'; break;
case KEY_8: digit = '8'; break;
case KEY_9: digit = '9'; break;
default: digit = '\0';
}
/* Non-digit key ends the code, except at beginning of code. */
if (digit == '\0') {
if (!len)
continue;
status = 0;
break;
}
if (len < length)
buffer[len] = digit;
len++;
continue;
} else
if (n == (ssize_t)0) {
status = ENOENT;
break;
} else {
status = EIO;
break;
}
}
/* Add terminator character to buffer. */
if (len + 1 < length)
buffer[len + 1] = '\0';
else
buffer[length - 1] = '\0';
/* Cancel timeout. */
it.it_value.tv_sec = 0;
it.it_value.tv_nsec = 0;
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 0L;
(void)timer_settime(dev->timer, 0, &it, NULL);
errno = status;
return len;
}
Here is an example program, example.c. You supply the input event device (I suggest using a symlink in /dev/input/by-id/ or /dev/input/by-path/ if your udev provides those, as event device indexes may not be stable across kernel versions and hardware boots), and the maximum duration you're willing to wait for/until next barcode.
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include "barcode.h"
#define BARCODE_MAXLEN 1023
int main(int argc, char *argv[])
{
barcode_dev dev;
unsigned long ms;
int status, exitcode;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s INPUT-EVENT-DEVICE IDLE-TIMEOUT\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program reads barcodes from INPUT-EVENT-DEVICE,\n");
fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barcode.\n");
fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear as\n");
fprintf(stderr, "inputs in the machine.\n");
fprintf(stderr, "You can at any time end the program by sending it a\n");
fprintf(stderr, "SIGINT (Ctrl+C), SIGHUP, or SIGTERM signal.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
{
double value, check;
char dummy;
if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) {
fprintf(stderr, "%s: Invalid idle timeout value (in seconds).\n", argv[2]);
return EXIT_FAILURE;
}
ms = (unsigned long)(value * 1000.0);
check = (double)ms / 1000.0;
if (value < check - 0.001 || value > check + 0.001 || ms < 1UL) {
fprintf(stderr, "%s: Idle timeout is too long.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (barcode_open(&dev, argv[1])) {
fprintf(stderr, "%s: Cannot open barcode input event device: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
while (1) {
char code[BARCODE_MAXLEN + 1];
size_t len;
if (done) {
status = EINTR;
break;
}
len = barcode_read(&dev, code, sizeof code, ms);
if (errno) {
status = errno;
break;
}
if (len < (size_t)1) {
status = ETIMEDOUT;
break;
}
printf("%zu-digit barcode: %s\n", len, code);
fflush(stdout);
}
if (status == EINTR) {
fprintf(stderr, "Signaled to exit. Complying.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else
if (status == ETIMEDOUT) {
fprintf(stderr, "Timed out, no more barcodes.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else {
fprintf(stderr, "Error reading input event device %s: %s.\n", argv[1], strerror(status));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
if (barcode_close(&dev)) {
fprintf(stderr, "Warning: Error closing input event device %s: %s.\n", argv[1], strerror(errno));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
return exitcode;
}
As you can see, it just prints the barcodes to standard output (and any error messages and warnings to standard error). To compile it, I recommend using the following Makefile (indentation must be using Tab, not spaces):
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS := -lrt
.PHONY: all clean
all: clean example
clean:
rm -f example *.o
%.o: %.c
$(CC) $(CFLAGS) -c $^
example: example.o barcode.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o example
To compile, create the four files listed above, then run
make clean example
Running for example
./example /dev/input/event4 5.0
will read barcodes from /dev/input/event4, but will exit at Ctrl+C (INT signal), HUP signal, TERM signal, or if no barcode appears within 5 seconds.
Note that if only a partial barcode is read within that 5 seconds, we do get that partial part (and could just try and read the rest of it), but the above example program ignores the partial, and only shows the timeout.
Questions?