C program keeps sending same data on serial port - c

I'm working on a SOM board running Linux embedded for ARM, and I'm developing a C program to communicate with an external device through a serial (RS232) port. I am experiencing a strange behavior though. I'm also using another serial port of the board to communicate with the linux running on the board.
The software has a simple structure: is a text-only console-like program, with this as main menu:
Possible commands:
1 - 4: Select serial device (pump should be on 1)
m - pump op. mode configuration
r - reads from the serial device
w - writes to the serial device
>>>>>>>>>>>>>>Current device is /dev/ttymxc1
>>>>>>>>>>>>>>Enter input (q quits):
and a secondary menu (opened by the "m" option above)
SPEED:
r - rpm (sends 1M<CR>) //<CR> stands for carriage return
f - flow rate (sends 1N<CR>)
QUANTITY:
v - volume (sends 1H<CR>)
t - time (sends 1O<CR>)
DIRECTION:
c - clockwise (sends 1T<CR>)
a - c-clockwise (sends 1K<CR>)
>>>>>>>>>>>>>>Enter input (q quits):
Communication using the main menu options "r" and "w" works fine (thus removing any doubt I may have regarding serial settings like baud rate): "w" invokes a routine ("serial_write" below) that sends a single character input by the user, while "r" returns the data read as soon it arrives (using "serial_read" below). The character I send arrives correctly, and the answer is shown correctly on the console, no matter the times I repeat the "w" and "r" cycle.
The options in the secondary menu should behave in the same way: they simply invoke a routine ("sendSimpleCmd" below) that invokes "serial_write" with a costant char as argument (different for each option), and then it invokes "serial_read".
The problem is that this only works for the first option selected: after that, the program keeps sending data linked to the first option selected, no matter the option I choose. It keeps doing this until I go back to the main menu, then choose again "m": at this point the data sent is the one I expect, but the subsequent choices will be ignored until I go back to the main menu (or close the software, if that matters).
The strangest thing is that I receive the expected data on the same serial I'm using to communicate with board while on the "right" serial port I keep getting the first message. This is the text pasted from the console when I choose "a" as second option, after having chosen "f" as first option (comments added by me):
SPEED:
r - rpm (sends 1M<CR>)
f - flow rate (sends 1N<CR>)
QUANTITY:
v - volume (sends 1H<CR>)
t - time (sends 1O<CR>)
DIRECTION:
c - clockwise (sends 1T<CR>)
a - c-clockwise (sends 1K<CR>)
>>>>>>>>>>>>>>Enter input (q quits):
a //second option
1Knding 1M //mixup of data
wrote 4 characters on fs 4
serial_read: *
The mixup is made by the software output ("Sending data 1M") and the data that should be sent after choosing option "a" (1K). Since on the "right" port I get the same message over and over, while on the "wrong" port I get the right message, it seems that somehow the software autonomously changes port.
The question is:
Could this behavior be caused by my coding, or is bound to something else, like some kernel configuration? If more information is needed, just ask.
Thank you in advance
Serial_write
void serial_write(char text[], int length){
if (selectedDevice == 0){
printf("Select device first!\r\n");
return;
}
int n;
length = length +1 + 2;
char toBeSent[length];
strcat(toBeSent, PUMP_CMD_MSG_START); //header, "1"
strcat(toBeSent, text);
strcat(toBeSent, PUMP_CMD_MSG_END); //footer, "<CR>"
printf("Sending %s\r\n", toBeSent);
n = write (fd, toBeSent, length);
if (n<0){
printf("writing failed on /dev/ttymxc%i\r\n", selectedDevice);
} else {
printf("wrote %i characters on fs %i\r\n", n, fd);
}
}
Serial_read
int serial_read(char *buffer, int size){
int bytes = 0;
int n;
int i = 0;
char tmp_buffer[size];
while(1){
ioctl(fd, FIONREAD, &bytes);
if (bytes > 0){
break;
}
i++;
if(i> 1000){
printf("FIONREAD tries exceeded 1000, aborting read\r\n");
return;
}
usleep(1000);
}
n=read(fd, tmp_buffer, sizeof(tmp_buffer));
for(i=0;i<n;i++) {
buffer[i]=tmp_buffer[i];
}
printf("serial_read: %s\r\n", buffer);
return 0;
}
sendSimpleCmd
void sendSimpleCmd(char text[]){
int bufSize= 20;
char answer[bufSize];
serial_write(text,1);
if (serial_read(answer, bufSize) == 0) {
printf("Ricevuto da pompa \"%s\":", answer);
//handling of possible answers, doesn't do anything relevant since it always receives "*" as answer
if (strcmp(answer, PUMP_ANS_OK) == 0){ //PUMP_ANS_OK is "*"
printf("ok!\r\n");
} else if (strcmp(answer, PUMP_ANS_NOK) == 0){
printf("errore!\r\n");
} else {
printf("sconosciuto!\r\n");
}
} else {
// printf("read failed\r\n");
}
}

You should be initializing toBeSent's contents before using strcat.
Your compiler might be saving you by initializing the array with 0s rather than garbage, but if not, it could be causing buffer overflows. Theres no code protecting against such, so this could be the cause of unexpected program behavior. Without seeing the rest of your code and knowing some other details, it will be difficult to know what exactly the issue is. If this serves as an example of the rest of the code, then the solution is likely to revise the code to fix these issues.
Consider using safe string functions to help prevent buffer overflows.

Related

attach a terminal to a process running as a daemon (to run an ncurses UI)

I have a (legacy) program which acts as a daemon (in the sense it runs forever waiting for requests to service) but which has an ncurses based user interface which runs on the host.
I would like to alter the program such that if I connect to the host via ssh I can enable the user interface on demand.
I know there is at least one way using pseudo-terminals but I'm not quite sure how to achieve it.
There are two application behaviours I consider interesting:
Run the UI only if the application is running in the foreground on a terminal
If the application runs in the foreground on a terminal - display the UI
If the application runs in the background - do not display the UI
If the application is moved to the background - close the UI
If the application is moved to the foreground of a terminal - open the UI
Create a new UI on demand when someone connects to the server
The application is running in the background
A new user logs in to the machine
They run something which causes an instance of the UI to open in their terminal
Multiple users can have their own instances of the UI.
Notes
There is a simple way to do this using screen. So:
original:
screen mydaemon etc...
new ssh session:
screen -d
screen -r
This detaches the screen leaving it running in the background and then reattches it to the current terminal. On closing the terminal the screen session becomes detached so this works quite well.
I'd like to understand what screen does under the hood, both for my own education and to understand how you would put some of that functionality into the application itself.
I know how I would do this for a server connected via a socket. What I would like to understand is how this could be done in principle with pseudo terminals. It is indeed a odd way to make an application work but I think it would serve to explore deeply the powers and limitations of using pseudo-terminals.
For case one, I assume I want the ncurses UI running in a slave terminal which the master side passing input to and from it.
The master process would use something like isatty() to check whether it is currently in the foreground of a terminal and activate or deactivate the UI using newterm() and endwin().
I've been experimenting with this but I have not got it to work yet as there are some aspects of terminals and ncurses that I have at best not got to grips with yet and at worst fundamental misunderstand.
Pseudo code for this is:
openpty(masterfd,slavefd)
login_tty();
fork();
ifslave
close(stdin)
close(stdout)
dup_a_new_stdin_from_slavefd();
newterm(NULL, newinfd, newoutfd); (
printw("hello world");
insert_uiloop_here();
endwin();
else ifmaster
catchandforwardtoslave(SIGWINCH);
while(noexit)
{
docommswithslave();
forward_output_as_appropriate();
}
Typically I either get a segfault inside fileno_unlocked() in newterm()
or output on the invoking terminal rather than a new invisible terminal.
Questions
What is wrong with the above pseudo code?
Do I have the master and slave ends the right way around?
What does login_tty actually do here?
Is there any practical difference between openpty() + login_tty() vs posix_openpt() + grantpt()?
Does there have to be a running process associated with or slave master tty at all times?
Note: This is a different question to ncurses-newterm-following-openpty which describes a particular incorrect/incomplete implementation for this use case and asks what is wrong with it.
This is a good question, and a good example of why we have pseudoterminals.
For the daemon to be able to use an ncurses interface, it requires a pseudoterminal (the slave side of a pseudoterminal pair), which is available from the point the daemon starts executing, continuously, until the daemon exits.
For a pseudoterminal to exist, there must be a process that has an open descriptor to the master side of the pseudoterminal pair. Additionally, it must consume all output from the pseudoterminal slave side (visible stuff output by ncurses). Usually, a library like vterm is used to interpret that output to "draw" the actual text framebuffer into an array (well, usually two arrays - one for the wide characters displayed in each cell (specific row and clumn), and another for the attributes like color).
For the pseudoterminal pair to work correctly, either the process at the master end is a parent or ancestor of the process running ncurses in the slave end, or the two are completely unrelated. The process running ncurses in the slave end should be in a new session, with the pseudoterminal as its controlling terminal. This is easiest to achieve, if we use a small pseudoterminal "server" that launches the daemon in a child process; and indeed, this is the pattern that is typically used with pseudoterminals.
The first scenario is not really feasible, because there is no parent/master process maintaining the pseudoterminal.
We can provide the behaviour of the first scenario, by adding a small pseudoterminal-providing "janitor" process, whose task is to maintain the pseudoterminal pair in existence, and to consume any ncurses output generated by the process running in the pseudoterminal pair.
However, that behavour also matches the second scenario.
Put another way, here is what would work:
Instead of launching the daemon directly, we use a custom program, say 'janitor', that creates a pseudoterminal and runs the daemon inside that pseudoterminal.
Janitor will stay running for as long as the daemon runs.
Janitor provides an interface for other processes to "connect" to the master side of the pseudoterminal pair.
This does not necessarily mean 1:1 proxying of data. Usually input (keypresses) to the daemon are provided unmodified, but how the contents of the pseudoterminal "framebuffer", the character-based virtual window contents, are transferred does vary. This is completely under our own control.
To connect to the janitor, we'll need a second helper program.
In the case of 'screen', these two programs are actually the same binary; the behaviour is just controlled by command-line parameters, and keypresses "consumed" by 'screen' itself, to control 'screen' behaviour and not passed to the actual ncurses-based process running in the pseudoterminal.
Thus far, we could just examine tmux or screen sources to see how they do the above; it is very straightforward terminal multiplexing stuff.
However, here we have a very interesting bit I had not considered before; this small bit made me understand the quite important core of this question:
Multiple users can have their own instances of the UI.
A process can only have one controlling terminal. This specifies a certain relationship. For example, when the master side of the controlling terminal is closed, the pseudoterminal pair vanishes, and the descriptors open to the slave side of the pseudoterminal pair become nonfunctional (all operations yield EIO, if I recall correctly); but more than that, every process in the process group receives a HUP signal.
The ncurses newterm() function lets a process connect to an existing terminal or pseudoterminal, at run time. That terminal does not need to be the controlling terminal, nor does the ncurses-using process need to belong to that session. It is important to realize that in this case, the standard streams (standard input, output, and error) are not redirected to the terminal.
So, if there is a way to tell a daemon that it has a new pseudoterminal available, and should open that because there is a user that wants to use the interface the daemon provides, we can have the daemon open and close the pseudoterminals on demand!
Note, however, that this requires explicit co-operation between the daemon, and the processes that are used to connect to the ncurses-based UI the daemon provides. There is no standard way of doing this with arbitrary ncurses-based processes or daemons. For example, as far as I know, nano and top provide no such interface; they only use the pseudoterminal associated with the standard streams.
After posting this answer – hopefully fast enough before the question is closed because others do not see the validity of the question, and its usefulness to other server-side POSIXy developers –, I shall construct an example program pair to exemplify the above; probably using an Unix domain socket as the "new UI for this user, please" communications channel, as file descriptors can be passed as ancillary data using Unix domain sockets, and identity of the user at either end of the socket can be verified (credentials ancillary data).
However, for now, let's go back to the questions asked.
What is wrong with the above pseudo code? [Typically I either get a segfault inside fileno_unlocked() in newterm() or output on the invoking terminal rather than a new invisible terminal.]
newinfd and newoutfd should be the same (or dup()s of) the pseudoterminal slave end file descriptor, slavefd.
I think there should also be an explicit set_term() with the SCREEN pointer returned by newterm() as a parameter. (It could be that it gets automatically called for the very first terminal provided by newterm(), but I'd rather call it explicitly.)
newterm() connects to and prepares a new terminal. The two descriptors usually both refer to the same slave side of a pseudoterminal pair; infd can be some other descriptor where the user keypresses are received from.
Only one terminal can be active in ncurses at a time. You need to use set_term() to select which one will be affected by following printw() etc. calls. (It returns the terminal that was previously active, so that one can do an update to another terminal and then return back to the original terminal.)
(This also means that if a program provides multiple terminals, it must cycle between them, checking for input, and update each terminal, at a relatively high frequency, so that human users feel the UI is responsive, and not "laggy". A crafty POSIX programmer can select or poll on the underlying descriptors, though, and only cycle through terminals that have input pending.)
Do I have the master and slave ends the right way around?
Yes, I do believe you do. Slave end is the one that sees a terminal, and can use ncurses. Master end is the one that provides keypresses, and does something with the ncurses output (say, draws them to a text-based framebuffer, or proxies to a remote terminal).
What does login_tty actually do here?
There are two commonly used pseudoterminal interfaces: UNIX98 (which is standardized in POSIX), and BSD.
With the POSIX interface, posix_openpt() creates a new pseudoterminal pair, and returns the descriptor to its master side. Closing this descriptor (the last open duplicate) destroys the pair. In the POSIX model, initially the slave side is "locked", and unopenable. unlockpt() removes this lock, allowing the slave side to be opened. grantpt() updates the character device (corresponding to the slave side of the pseudoterminal pair) ownership and mode to match the current real user. unlockpt() and grantpt() can be called in either order, but it makes sense to call grantpt() first; that way the slave side cannot be opened "accidentally" by other processes, before its ownership and access mode have been set properly. POSIX provides the path to the character device corresponding to the slave side of the pseudoterminal pair via ptsname(), but Linux provides an TIOCGPTPEER ioctl (in kernels 4.13 and later) that allows opening the slave end even if the character device node is not shown in the current mount namespace.
Typically, grantpt(), unlockpt(), and opening the slave side of the pseudoterminal pair are done in a child process (that still has access to the master-side descriptor) that has started a new session using setsid(). The child process redirects standard streams (standard input, output, and error) to the slave side of the pseudoterminal, closes its copy of the master-side descriptor, and makes sure the pseudoterminal is its controlling terminal. Usually this is followed by executing the binary that will use the pseudoterminal (usually via ncurses) for its user interface.
With the BSD interface, openpty() creates the pseudoterminal pair, providing open file descriptors to both sides, and optionally sets the pseudoterminal termios settings and window size. It roughly corresponds to POSIX posix_openpt() + grantpt() + unlockpt() + opening the slave side of the pseudoterminal pair + optionally setting the termios settings and terminal window size.
With the BSD interface, login_tty is run in the child process. It runs setsid() to create a new session, makes the slave side the controlling terminal, redirects standard streams to the slave side of the controlling terminal, and closes the copy of the master side descriptor.
With the BSD interface, forkpty() combines openpty(), fork(), and login_tty(). It returns twice; once in the parent (returning the PID of the child process), and once in the child (returning zero). The child is running in a new session, with the pseudoterminal slave side as its controlling terminal, already redirected to the standard streams.
Is there any practical difference between openpty() + login_tty() vs posix_openpt() + grantpt() [ + unlockpt() + opening the slave side]?
No, not really.
Both Linux and most BSDs tend to provide both. (In Linux, when using the BSD interface, you need to link in the libutil library (-lutil gcc option), but it is provided by the same package that provides the standard C library, and can be assumed to be always available.)
I tend to prefer the POSIX interface, even though it is lots more verbose, but other than kinda preferring POSIX interfaces over BSD ones, I don't even know why I prefer it over the BSD interface. The BSD forkpty() does basically everything for the most common use cases in one call!
Also, instead of relying on ptsname() (or the GNU ptsname_r() extension), I tend to first try the Linux-specific ioctl if it looks like it is available, and fall back to ptsname() if it is not available. So, if anything, I probably should prefer the BSD interface.. but the libutil kinda sorta annoys me a little bit, I guess, so I don't.
I definitely have no objection to others preferring the BSD interface. If anything, I'm a bit puzzled as to how my preference even exists; normally I prefer the simpler, more robust interfaces over the verbose, complex ones.
Does there have to be a running process associated with or slave master tty at all times?
There has to be a process having the master side of the pseudoterminal open. When the last duplicate of the descriptor is closed, the kernel destroys the pair.
Also, if the process having the master side descriptor does not read from it, the process running in the pseudoterminal will unexpectedly block in some ncurses call. Normally, the calls do not block (or only block for very short durations, shorter than what humans notice). If the process just reads but discards the input, then we do not actually know the contents of the ncurses terminal!
So, we can say that having a process that reads from the pseudoterminal pair master side, keeping a descriptor open to the master side, is absolutely required.
(The slave side is different; because the character device node is usually visible, a process can close its connection to the pseudoterminal temporarily, and just reopen it later. In Linux, when no process has an open descriptor to the slave side, the process reading from or writing to the master side will get EIO errors (read() and write() returning -1 with errno==EIO). I'm not absolutely certain if this is guaranteed behaviour, though; haven't thus far ever relied on it, and only noticed it recently (when implementing an example) myself.
Here is an example of an ncurses application that animates a bouncing X on each terminal supplied as a parameter:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/ioctl.h>
#include <locale.h>
#include <curses.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#ifndef FRAMES_PER_SECOND
#define FRAMES_PER_SECOND 25
#endif
#define FRAME_DURATION (1.0 / (double)(FRAMES_PER_SECOND))
/* Because the terminals are not the controlling terminal for this process,
* this process may not receive the SIGWINCH signal whenever a screen size
* changes. Therefore, we call this function to update it whenever we switch
* between terminals.
*/
extern void _nc_update_screensize(SCREEN *);
/*
* Signal handler to notice if this program - all its terminals -- should exit.
*/
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = signum;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
return sigaction(signum, &act, NULL);
}
/* Difference in seconds between to timespec structures.
*/
static inline double difftimespec(const struct timespec after, const struct timespec before)
{
return (double)(after.tv_sec - before.tv_sec)
+ (double)(after.tv_nsec - before.tv_nsec) / 1000000000.0;
}
/* Sleep the specified number of seconds using nanosleep().
*/
static inline double nsleep(const double seconds)
{
if (seconds <= 0.0)
return 0.0;
const long sec = (long)seconds;
long nsec = (long)(1000000000.0 * (seconds - (double)sec));
if (nsec < 0)
nsec = 0;
if (nsec > 999999999)
nsec = 999999999;
if (sec == 0 && nsec < 1)
return 0.0;
struct timespec req = { .tv_sec = (time_t)sec, .tv_nsec = nsec };
struct timespec rem = { .tv_sec = 0, .tv_nsec = 0 };
if (nanosleep(&req, &rem) == -1 && errno == EINTR)
return (double)(rem.tv_sec) + (double)(rem.tv_nsec) / 1000000000.0;
return 0.0;
}
/*
* Structure describing each client (terminal) state.
*/
struct client {
SCREEN *term;
FILE *in;
FILE *out;
int col; /* Ball column */
int row; /* Ball row */
int dcol; /* Ball direction in column axis */
int drow; /* Ball direction in row axis */
};
static size_t clients_max = 0;
static size_t clients_num = 0;
static struct client *clients = NULL;
/* Add a new terminal, based on device path, and optionally terminal type.
*/
static int add_client(const char *ttypath, const char *term)
{
if (!ttypath || !*ttypath)
return errno = EINVAL;
if (clients_num >= clients_max) {
const size_t temps_max = (clients_num | 15) + 13;
struct client *temps;
temps = realloc(clients, temps_max * sizeof clients[0]);
if (!temps)
return errno = ENOMEM;
clients_max = temps_max;
clients = temps;
}
clients[clients_num].term = NULL;
clients[clients_num].in = NULL;
clients[clients_num].out = NULL;
clients[clients_num].col = 0;
clients[clients_num].row = 0;
clients[clients_num].dcol = +1;
clients[clients_num].drow = +1;
clients[clients_num].in = fopen(ttypath, "r+");
if (!clients[clients_num].in)
return errno;
clients[clients_num].out = fopen(ttypath, "r+");
if (!clients[clients_num].out) {
const int saved_errno = errno;
fclose(clients[clients_num].in);
return errno = saved_errno;
}
clients[clients_num].term = newterm(term, clients[clients_num].in,
clients[clients_num].out);
if (!clients[clients_num].term) {
fclose(clients[clients_num].out);
fclose(clients[clients_num].in);
return errno = ENOMEM;
}
set_term(clients[clients_num].term);
start_color();
cbreak();
noecho();
nodelay(stdscr, TRUE);
keypad(stdscr, TRUE);
scrollok(stdscr, FALSE);
curs_set(0);
clear();
refresh();
clients_num++;
return 0;
}
static void close_all_clients(void)
{
while (clients_num > 0) {
clients_num--;
if (clients[clients_num].term) {
set_term(clients[clients_num].term);
endwin();
delscreen(clients[clients_num].term);
clients[clients_num].term = NULL;
}
if (clients[clients_num].in) {
fclose(clients[clients_num].in);
clients[clients_num].in = NULL;
}
if (clients[clients_num].out) {
fclose(clients[clients_num].out);
clients[clients_num].out = NULL;
}
}
}
int main(int argc, char *argv[])
{
struct timespec curr, prev;
int arg;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s TERMINAL [ TERMINAL ... ]\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This program displays a bouncing ball animation in each terminal.\n");
fprintf(stderr, "Press Q or . in any terminal, or send this process an INT, HUP,\n");
fprintf(stderr, "QUIT, or TERM signal to quit.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
setlocale(LC_ALL, "");
for (arg = 1; arg < argc; arg++) {
if (add_client(argv[arg], NULL)) {
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
close_all_clients();
return EXIT_FAILURE;
}
}
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGQUIT) == -1 ||
install_done(SIGTERM) == -1) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
close_all_clients();
return EXIT_FAILURE;
}
clock_gettime(CLOCK_MONOTONIC, &curr);
while (!done && clients_num > 0) {
size_t n;
/* Wait until it is time for the next frame. */
prev = curr;
clock_gettime(CLOCK_MONOTONIC, &curr);
nsleep(FRAME_DURATION - difftimespec(curr, prev));
/* Update each terminal. */
n = 0;
while (n < clients_num) {
int close_this_terminal = 0;
int ch, rows, cols;
set_term(clients[n].term);
/* Because the terminal is not our controlling terminal,
we may miss SIGWINCH window size change signals.
To work around that, we explicitly check it here. */
_nc_update_screensize(clients[n].term);
/* Process inputs - if we get any */
while ((ch = getch()) != ERR)
if (ch == 'x' || ch == 'X' || ch == 'h' || ch == 'H')
clients[n].dcol = -clients[n].dcol;
else
if (ch == 'y' || ch == 'Y' || ch == 'v' || ch == 'V')
clients[n].drow = -clients[n].drow;
else
if (ch == '.' || ch == 'q' || ch == 'Q')
close_this_terminal = 1;
if (close_this_terminal) {
endwin();
delscreen(clients[n].term);
fclose(clients[n].in);
fclose(clients[n].out);
/* Remove from array. */
clients_num--;
clients[n] = clients[clients_num];
clients[clients_num].term = NULL;
clients[clients_num].in = NULL;
clients[clients_num].out = NULL;
continue;
}
/* Obtain current terminal size. */
getmaxyx(stdscr, rows, cols);
/* Leave a trace of dots. */
if (clients[n].row >= 0 && clients[n].row < rows &&
clients[n].col >= 0 && clients[n].col < cols)
mvaddch(clients[n].row, clients[n].col, '.');
/* Top edge bounce. */
if (clients[n].row <= 0) {
clients[n].row = 0;
clients[n].drow = +1;
}
/* Left edge bounce. */
if (clients[n].col <= 0) {
clients[n].col = 0;
clients[n].dcol = +1;
}
/* Bottom edge bounce. */
if (clients[n].row >= rows - 1) {
clients[n].row = rows - 1;
clients[n].drow = -1;
}
/* Right edge bounce. */
if (clients[n].col >= cols - 1) {
clients[n].col = cols - 1;
clients[n].dcol = -1;
}
clients[n].row += clients[n].drow;
clients[n].col += clients[n].dcol;
mvaddch(clients[n].row, clients[n].col, 'X');
refresh();
/* Next terminal. */
n++;
}
}
close_all_clients();
return EXIT_SUCCESS;
}
This contains no pseudoterminals, and the only real quirk is the use of _nc_update_screensize() to detect if any of the terminals have changed. (Because they are not our controlling terminal, we do not receive the SIGWINCH signal, and thus ncurses misses the window change.)
I recommend compiling this with gcc -Wall -Wextra -O2 bounce.c -lncurses -o bounce.
Open a couple of terminal windows, and run tty to see the path to their controlling terminals (usually slave ends of pseudoterminals, /dev/pts/N).
Run ./bounce with one or more of those paths as parameters, and let the bouncing commence.
If you don't want the shell in a window to consume your input, and want the above program to see it, run e.g. sleep 6000 in the terminal windows before running the above command.
This program simply opens two streams to each terminal, and lets ncurses take control of them; basically, it is an example of a multi-terminal ncurses application, and how to juggle them using newterm(), set_term() and so on.
If you supply the same terminal more than once, pressing Q closes them in a random order, so ncurses may not revert the terminal to the original state correctly. (You may need to type reset blindly, to reset the terminal to a workable state; it's a companion command to clear, which just clears the terminal. They don't do anything else, just terminal stuff.)
Instead of providing the paths to the terminal devices as a command-line parameter, the program could just as well run all the time, but listen for incoming Unix domain datagrams, with SOL_SOCKET-level SCM_RIGHTS-type ancillary data, that can be used to duplicate file descriptors between unrelated processes.
However, if one relinguishes the control of a terminal like that (either by opening the terminal, or by passing the terminal file descriptor to another process), the problem is that it is impossible to revoke that access. We can avoid that by using a pseudoterminal in between, and proxying the data between the pseudoterminal and our real terminal. To break the connection, we simply stop proxying data and destroy the pseudoterminal pair, and revert our terminal to its initial state.
Examining the above program, we see that the procedure in pseudocode for taking control of a new terminal is
Obtain two FILE stream handles to the terminal.
The above program uses fopen() to open them like normal. Other programs can use dup() to duplicate a single descriptor, and fdopen() to convert them to stdio FILE stream handles.
Call SCREEN *term = newterm(NULL, in, out) to let ncurses know about this new terminal.
in and out are the two FILE stream handles. The first parameter is the terminal type string; if NULL, the TERM environment variable is used instead. A typical value today is xterm-256color, but ncurses supports a lot of other types of terminals also.
Call set_term(term) to make the new terminal the currently active one.
At this point, we can do normal ncurses setup stuff, like cbreak(); noecho(); and so on.
Relinguishing control of a terminal is also simple:
Call set_term(term) to make that terminal the currently active one.
Call endwin() and delscreen(term).
Close the two FILE streams to the terminal.
Updating terminal contents requires a loop, with each iteration handling one terminal, starting with a set_term(term) call (followed by the _nc_update_screensize(term) call, if we wish to react to window size changes in those terminals).
The above example program uses nodelay() mode, so that getch() will return either a keypress, or ERR if there is no pending input from the current terminal. (At least in Linux, we will get KEY_RESIZE whenever the window size changes, as long as either the terminal is our controlling terminal, or we call _nc_update_screensize().)
But note: if there are other processes also reading from that terminal, say a shell, the input could be read by any of the processes.

Is it OK to loop over recv / read to read all data from socket

I'm building a multi-client<->server messaging application over TCP.
I created a non blocking server using epoll to multiplex linux file descriptors.
When a fd receives data, I read() /or/ recv() into buf.
I know that I need to either specify a data length* at the start of the transmission, or use a delimiter** at the end of the transmission to segregate the messages.
*using a data length:
char *buffer_ptr = buffer;
do {
switch (recvd_bytes = recv(new_socket, buffer_ptr, rem_bytes, 0)) {
case -1: return SOCKET_ERR;
case 0: return CLOSE_SOCKET;
default: break;
}
buffer_ptr += recvd_bytes;
rem_bytes -= recvd_bytes;
} while (rem_bytes != 0);
**using a delimiter:
void get_all_buf(int sock, std::string & inStr)
{
int n = 1, total = 0, found = 0;
char c;
char temp[1024*1024];
// Keep reading up to a '\n'
while (!found) {
n = recv(sock, &temp[total], sizeof(temp) - total - 1, 0);
if (n == -1) {
/* Error, check 'errno' for more details */
break;
}
total += n;
temp[total] = '\0';
found = (strchr(temp, '\n') != 0);
}
inStr = temp;
}
My question: Is it OK to loop over recv() until one of those conditions is met? What if a client sends a bogus message length or no delimiter or there is packet loss? Wont I be stuck looping recv() in my program forever?
Is it OK to loop over recv() until one of those conditions is met?
Probably not, at least not for production-quality code. As you suggested, the problem with looping until you get the full message is that it leaves your thread at the mercy of the client -- if a client decides to only send part of the message and then wait for a long time (or even forever) without sending the last part, then your thread will be blocked (or looping) indefinitely and unable to serve any other purpose -- usually not what you want.
What if a client sends a bogus message length
Then you're in trouble (although if you've chosen a maximum-message-size you can detect obviously bogus message-lengths that are larger than that size, and defend yourself by e.g. forcibly closing the connection)
or there is packet loss?
If there is a reasonably small amount of packet loss, the TCP layer will automatically retransmit the data, so your program won't notice the difference (other than the message officially "arriving" a bit later than it otherwise would have). If there is really bad packet loss (e.g. someone pulled the Ethernet cable out of the wall for 5 minutes), then the rest of the message might be delayed for several minutes or more (until connectivity recovers, or the TCP layer gives up and closes the TCP connection), trapping your thread in the loop.
So what is the industrial-grade, evil-client-and-awful-network-proof solution to this dilemma, so that your server can remain responsive to other clients even when a particular client is not behaving itself?
The answer is this: don't depend on receiving the entire message all at once. Instead, you need to set up a simple state-machine for each client, such that you can recv() as many (or as few) bytes from that client's TCP socket as it cares to send to you at any particular time, and save those bytes to a local (per-client) buffer that is associated with that client, and then go back to your normal event loop even though you haven't received the entire message yet. Keep careful track of how many valid received-bytes-of-data you currently have on-hand from each client, and after each recv() call has returned, check to see if the associated per-client incoming-data-buffer contains an entire message yet, or not -- if it does, parse the message, act on it, then remove it from the buffer. Lather, rinse, and repeat.

Streaming engines best practice in C

LANG: C / ENV: Linux
I am developing a streaming engine, for now I am able to start, stop and pause the stream, but seeking is the operation that's giving me a lot of headache, I already asked a question here before and fixed some issues inside the code from the answers.
Using lseek() function, I am passing the open streaming file descriptor as first argument, plus I am using UDP for transmitting, something like the following code:
transport_fd = open(tsfile, O_RDONLY);
int offset = 1024;
off_t offsetIndicator;
if ((offsetIndicator=lseek(transport_fd, offset, SEEK_CUR))<0) printf("Error seeking\n");
Whenever I try to seek while streaming, the streaming stops and the pictures hangs.
Is there anything I should pay attention to?, i.e: like attempting to sleep() or nanosleep() after seeking into the file in order for the changes to take effect.
I couldn't find examples, papers or realted articles for best practices in such engines.
EDIT:
After testing, it seems like the file continued to stream but receiving devices on the network didn't catch the stream connection anymore, and calculating the time it took to finish after subtract seeking time, the stream seems to be finished normally.
CODE SNIPPET:
while (!completed)
{
while (/* Comparing conditions */ && !completed)
{
if (seekLck == 1) // seekLck is a semaphore to test seek signal from father process initiated by 0
{
int offset = 1024;
off_t offsetIndicator;
if ((offsetIndicator=lseek(transport_fd, offset, SEEK_CUR))<0)
printf("Error seeking\n");
nanosleep(&nano_sleep_packet, 0); //Try to sleep to see if it is still hanging, didn't work
seekLck = 0;
}
len = read(transport_fd, send_buf, packet_size);
if(len < 0) {
fprintf(stderr, "File read error \n");
completed = 1;
}
else if (len == 0)
{
fprintf(stderr, "Sent done\n");
completed = 1;
}
else
{
sent = sendto(sockfdstr, send_buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if(sent <= 0)
{
perror("send(): error ");
completed = 1;
}
}
}
nanosleep(&nano_sleep_packet, 0);
}
close(transport_fd);
close(sockfdstr);
free(send_buf);
printf("cleaning up\n");
return 0;
}
The main question was "Why isn't the file being streamed (Played) even when lseek() is working fine?"...
Actually nothing was wrong from the server side, but even though, the clients weren't able to continue streaming after losing the frame count (streaming ffmpeg frames, clients are getting the stream from a video scrambler).
What worked for me in this situation is getting the socket parameter and killing (in a clean way) the process that needs to be seeked in while holding the stream position, after that start a totally new stream from the seek position with the same socket parameter so it replaces the old one.
I hope this will help someone out there especially that there's no much documentation about those stuff.

Serial communication buffer data out of order

I'm trying to implement a simple SLAM project with Arduino and C on Linux Mint 15.
The Arduino project is sending data to notebook via bluetooth (serial). The data is read by a C program. In Arduino serial, the data is shown correctly, but in the notebook, the received data is wrong. (In image, white is Arduino data. The terminal shows the 'received' data.)
I'm sending d080x096y099z035 (for example) and receiving 99z0356y0999z035 (out of order?).
So, I have some questions:
What can I do to make the read()command in C, read the data in the correct order and length? (order: d000x000y000z000, length = 16)
In the Arduino sending function, are there length differences using Serial.print(char buffer[]) and Serial.println(char buffer[])? (Like adding a '\n' or something else at the end of buffer?)
Should I use the delay() function in the Arduino code or in the C code?
In Arduino:
...
int buffer_size = 17;
char buffer[17];
//void setup()
void loop(){
//create the string resp = "d000x111y222z333"
...
resp.toCharArray(buffer, buffersize);
bluetooth.print(buffer);
delay(200);
}
In C program:
...
int fd = open("/dev/rfcomm4", O_RDONLY | O_NOCTTY | O_NDELAY);
printf("fd code %d\n", fd);
if (fd == -1)
{
gchar *msg = "open_port: Unable to open /dev/rfcomm4";
gtk_label_set_text(GTK_LABEL(label), msg);
perror("error: ");
}
char buffer[17];
int n;
printf("entering in loop...\n");
while (1)
{
n = read(fd, buffer, sizeof(buffer));
printf("%s\n", buffer);
}
Sorry I'm not an expert but just a few ideas you might check concering your questions:
to 1) I guess it might be a problem with encoding, as Python AFAIK expects files to be unicode. So try open (.... ,encoding='ascii') or whatever encoding you use
Please also pay attention that you might block the GTK mainthread, that causes heavy delays in your UI. So I recommend creating a own thread for reading the serial port and filling an internal buffer, that get's rendered by the GTK mainthread, if you send an update request:
http://www.pardon-sleeuwaegen.be/antoon/python/page0.html

How can I add code to a libnodave test program (testISO_TCP simplified) that protects the read routine from crashing?

I will start by saying that I am a college student with little c++ experience. How many times have you heard that right? I am working with the test program testISO_TCP (simplified version) from the libnodave library. This program does a simple read of flag values and data blocks while it is connected to a seimens 300 PLC. The program doesn't bring up any errors per se. What I am trying to do is hopefully add some code to this program that will protect the reads from ever crashing. Let me explain a little better. Say for example I have a lot of reads implemented in the code. As of now there are only two reads. Eventually I will run this code with many more reads. Now, say that I am running the test program and for some reason I lose the connection to the PLC. I would like to have the program do one of two things: 1) Once the connection is lost, do a retry connect a certain amount of times and when it runs out of tries, exit. or 2) Somehow continue to read from the PLC until they are all done.
I hope this is enough information to get some help. I will post the code that I have been looking at for so long without any idea how to do this effectively. Thanks to all in advance.
#define PLAY_WITH_KEEPALIVE
#include <stdlib.h>
#include <stdio.h>
#include "nodavesimple.h"
#include "openSocket.h"
#ifdef PLAY_WITH_KEEPALIVE
#include <winsock.h>
#endif
int main(int argc, char **argv) {
int a,b,c,res, doRun, doStop, doRead, doreadFlag, useProtocol, useSlot;
#ifdef PLAY_WITH_KEEPALIVE
int opt;
#endif
float d;
daveInterface * di;
daveConnection * dc;
_daveOSserialType fds;
doRun=0;
doStop=0;
doRead=0;
doreadFlag=0;
useProtocol=daveProtoISOTCP;
useSlot=2;
fds.rfd=openSocket(102, argv[1]);
#ifdef PLAY_WITH_KEEPALIVE
errno=0;
opt=1;
//res=setsockopt(fds.rfd, SOL_SOCKET, SO_KEEPALIVE, &opt, 4);
//LOG3("setsockopt %s %d\n", strerror(errno),res);
#endif
fds.wfd=fds.rfd;
if (fds.rfd>0)
{
di =daveNewInterface(fds,"IF1",0, daveProtoISOTCP, daveSpeed187k);
daveSetTimeout(di,5000000);
dc =daveNewConnection(di,2,0, 2); // insert your rack and slot here
if (0==daveConnectPLC(dc))
{
printf("Connected.\n");
res=daveReadBytes(dc,daveFlags,0,0,16,NULL);
if (0==res)
{
a=daveGetU32(dc);
b=daveGetU32(dc);
c=daveGetU32(dc);
d=daveGetFloat(dc);
printf("FD0: %d\n",a);
printf("FD4: %d\n",b);
printf("FD8: %d\n",c);
printf("FD12: %f\n",d);
}//end 0==res
}//end daveConnectPLC
else
{
printf("Couldn't connect to PLC.\n Please make sure you use the -2 option with a CP243 but not with CPs 343 or 443.\n");
//closeSocket(fds.rfd);
//return -2;
}
}//end fds.rfd
fds.rfd=openSocket(102, argv[1]);
fds.wfd=fds.rfd;
if (fds.rfd>0)
{
di =daveNewInterface(fds,"IF1",0, daveProtoISOTCP, daveSpeed187k);
daveSetTimeout(di,5000000);
dc =daveNewConnection(di,2,0, 2); // insert your rack and slot here
if (0==daveConnectPLC(dc))
{
printf("Connected.\n");
res=daveReadBytes(dc,daveDB,1,0,64,NULL);
if (0==res)
{
a=daveGetU16(dc);
printf("DB1:DW0: %d\n",a);
a=daveGetU16(dc);
printf("DB1:DW1: %d\n...\n",a);
a=daveGetU16At(dc,62);
printf("DB1:DW32: %d\n",a);
}//end 0==res
return 0;
}//end daveConnectPLC
else
{
printf("Couldn't connect to PLC.\n Please make sure you use the -2 option with a CP243 but not with CPs 343 or 443.\n");
closeSocket(fds.rfd);
return -2;
}
}//end fds.rfd
else
{
printf("Couldn't open TCP port. \nPlease make sure a CP is connected and the IP address is ok. \n");
return -1;
}
}// end main
You have to check the return value of the daveReadBytes function.
If it is not zero, something went wrong and you can use the daveStrerror function to get a proper error message:
printf ("error: %s\n", daveStrerror(res));
After that it's up to you to decide to either simply retry the read or disconnect (with closeSocket(...)) and then create a new connection from the beginning. Check the documentation on what errorcodes there are. Some errors can't be resolved by retrying (e.g. because you try reading a data block that doesn't exist).
I have a loop that attempts to connect 3 times and exits gracefully if it fails
You may be able to write some other code to first check to see if the connection is up and also if the PLC is up.
Typically if you try to connect to an IP address that doesn't esond; it will hang there and tie up resources...
I am also new programmer.But want to say that. First we have to differentiate between the TCP/IP connection with the ethernet card ISO_TCP. The openSocket() function does the connection to remote IP adress in the given port/Service (102 ISO_TCP). When called next, the function daveNewInterface(), it will initialise the specific interface for doing a connection to the PLC. After this, the function daveNewConnection() tries to open a connection on a given MPI adress, and very important, the given rack and slot. If this function returns the value 0, it will call the daveConnectPLC() function to connect to the PLC. At this point it´s established a ethernet connection,and also the PLC Connection.
Now you can use all the function from the libnodave library for read or write data, stop or run the PLC and much more.
In the actually simplified TCP_ISO code there are no function to disconnect the adapter or close the connection with the PLC, in your code there is the closeSocket() function and also the function that returns -2. Find at what line the code breaks, introducing for example a log after every function to see the returns values.
All info for detecting loss of communication is in the documentation.

Resources