Implementation of embedded linux output redirection (serial port telnet mutual switching)

For scenarios such as batch on-hook testing of equipment, remote customer complaint handling, etc., it is still very practical to add a serial port redirection to the telnet port printing function. For this reason, it is packaged into an independent file. Not much to say, just copy it into the project and use it directly.

The C file code is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <errno.h>
#include "ls_output_redirect.h"

#define OUTPUT_PORT 401

static int g_redirect_running = 0;
static int g_listen_fd = -1;
static int g_client_fd = -1;
static pthread_t g_listen_pid = 0;
static pthread_t g_recv_pid = 0;
static int g_serial_fd = -1;
static int g_serial_err_fd = -1;
static int g_telnet_fd = -1;
static int g_telnet_err_fd = -1;
static int g_client_data_fd = -1;

/// @brief output redirected to the serial port
/// @param
/// @return
int __LS_OUTPUT_REDIRECT_Redirect2Serial(void)
{
    if (g_client_data_fd < 0)
    {
        return -1;
    }
    if (dup2(g_serial_fd, STDOUT_FILENO) < 0)
    {
        printf("%s %d: g_serial_fd dup2 STDOUT_FILENO failed\\
", __func__, __LINE__);
    }

    if (dup2(g_serial_err_fd, STDERR_FILENO) < 0)
    {
        printf("%s %d: g_serial_fd dup2 STDOUT_FILENO failed\\
", __func__, __LINE__);
    }

    close(g_client_data_fd);
    close(g_serial_fd);
    close(g_serial_err_fd);
    g_client_data_fd = -1;
    g_serial_fd = -1;
    return 0;
}

/// @brief output redirected to TCP
/// @param
/// @return
int __LS_OUTPUT_REDIRECT_Redirect2Tcp(int fd)
{
    if (g_client_data_fd >= 0)
    {
        return -1;
    }

    g_client_data_fd = fd;

    g_serial_fd = dup(STDOUT_FILENO);
    g_telnet_fd = dup2(g_client_data_fd, STDOUT_FILENO);

    g_serial_err_fd = dup(STDERR_FILENO);
    g_telnet_err_fd = dup2(g_client_data_fd, STDERR_FILENO);

    if (g_serial_fd < 0 || g_telnet_fd < 0)
    {
        perror("err in dup STDOUT.\\
");
        close(g_client_data_fd);
        g_client_data_fd = -1;
        return -4;
    }

    if (g_serial_err_fd < 0 || g_telnet_err_fd < 0)
    {
        perror("err in dup STDOUT.\\
");
        close(g_client_data_fd);
        g_client_data_fd = -1;
        return -4;
    }

    return 0;
}

/// @brief TCP data receiving thread
/// @param
/// @return
static void *__LS_OUTPUT_REDIRECT_SocketDataProc(void *arg)
{
    int max_fd = -1;

    fd_set readFdSet;
    fd_set writeFdSet;
    fd_set fdExcept;
    struct timeval waitTime = {0, 0};

    int RecvLen = 0;
    char *RecvBuffer = (char *)malloc(10240);
    while (g_redirect_running)
    {
        if (g_client_fd <= 0)
        {
            usleep(200000);
            continue;
        }

        waitTime.tv_sec = 0;
        waitTime.tv_usec = 500000;
        max_fd = g_client_fd;

        FD_ZERO( &readFdSet);
        FD_ZERO( &writeFdSet);
        FD_ZERO( & fdExcept);

        FD_SET(g_client_fd, &readFdSet);
        FD_SET(g_client_fd, &writeFdSet);
        FD_SET(g_client_fd, &fdExcept);

        switch (select(max_fd + 1, &readFdSet, &writeFdSet, &fdExcept, &waitTime))
        {
        case-1:
        case 0:
            usleep(500000);
            continue;
        default:
        {
            if (FD_ISSET(g_client_fd, &readFdSet))
            {
                memset(RecvBuffer, 0, 10240);

                RecvLen = recv(g_client_fd, RecvBuffer, 10240, 0);

                if (RecvLen <= 0)
                {
                    __LS_OUTPUT_REDIRECT_Redirect2Serial();
                    close(g_client_fd);
                    printf("%s %d: client %d disconnect\\
", __func__, __LINE__, g_client_fd);
                    g_client_fd = -1;
                    usleep(500000);
                    continue;
                }
            }
        }
        }
        usleep(500000);
    }

    free(RecvBuffer);
    pthread_detach(pthread_self());
    printf("%s %d: Thread end!\\
", __func__, __LINE__);
    return NULL;
}

/// @brief Listening TCP connection thread
/// @param
/// @return
static void *__LS_OUTPUT_REDIRECT_SocketListenProc(void *arg)
{
    fd_set fdSet;
    struct sockaddr_in peerAddr;
    struct timeval waitTime = {1, 5000};
    int len = sizeof(struct sockaddr);
    int sendBufSize = 512 * 1024;
    int flag = 1;

    while (g_redirect_running)
    {
        FD_ZERO( & fdSet);
        FD_CLR(g_listen_fd, &fdSet);
        FD_SET(g_listen_fd, & fdSet);

        switch (select(g_listen_fd + 1, &fdSet, NULL, NULL, &waitTime))
        {
        case-1:
        case 0:
            usleep(500 * 1000);
            continue;
        default:
            break;
        }

        if (FD_ISSET(g_listen_fd, &fdSet))
        {
            if (g_client_fd > 0)
            {
                usleep(200000);
                continue;
            }

            g_client_fd = accept(g_listen_fd, (struct sockaddr *) & amp;peerAddr, (socklen_t *) & amp;len);
            printf("%s %d: New connection ip = %s, port = %d\\
", __func__, __LINE__, inet_ntoa(peerAddr. sin_addr), peerAddr. sin_port);

            if (g_client_fd < 0)
            {
                printf("%s %d: Accept fd < 0\\
", __func__, __LINE__);
                sleep(1);
                continue;
            }

            fcntl(g_client_fd, F_SETFL, O_NONBLOCK);
            setsockopt(g_client_fd, SOL_SOCKET, SO_SNDBUF, & sendBufSize, sizeof(sendBufSize));
            setsockopt(g_client_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));

            __LS_OUTPUT_REDIRECT_Redirect2Tcp(g_client_fd);
        }
        usleep(500000);
    }

    close(g_listen_fd);
    g_listen_fd = -1;

    pthread_detach(pthread_self());
    printf("%s %d: Thread end!\\
", __func__, __LINE__);
    return NULL;
}

/// @brief Create a TCP socket
/// @param
/// @return
static int __LS_SocketCreate(unsigned short port)
{
    struct sockaddr_in servaddr;
    int reuse = 1;

    memset( &servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr. sin_port = htons(port);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if ((g_listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("%s %s %d: Create socket error. errno = %d\\
", __FILE__, __func__, __LINE__, errno);
        return -1;
    }

    if (setsockopt(g_listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
    {
        printf("%s %s %d: Set SO_REUSEADDR error. errno = %d\\
", __FILE__, __func__, __LINE__, errno);
        return -1;
    }

    if (bind(g_listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) != 0)
    {
        printf("%s %s %d: Bind error. errno = %d\\
", __FILE__, __func__, __LINE__, errno);
        return -1;
    }

    if (listen(g_listen_fd, 1) != 0)
    {
        printf("%s %s %d: Start listen error. errno = %d\\
", __FILE__, __func__, __LINE__, errno);
        return -1;
    }

    pthread_create( & g_listen_pid, NULL, __LS_OUTPUT_REDIRECT_SocketListenProc, NULL);
    pthread_create( & g_recv_pid, NULL, __LS_OUTPUT_REDIRECT_SocketDataProc, NULL);

    return 0;
}

/// @brief output redirection initialization
/// @param
/// @return
int LS_OUTPUT_REDIRECT_Init()
{
    __LS_SocketCreate(OUTPUT_PORT);
    g_redirect_running = 1;
    return 0;
}

/// @brief output redirection to initialize
/// @param
/// @return
int LS_OUTPUT_REDIRECT_Deinit()
{
    g_redirect_running = 0;
    pthread_join(g_listen_pid, NULL);
    pthread_join(g_recv_pid, NULL);
    if (g_listen_fd != -1)
    {
        close(g_listen_fd);
        g_listen_fd = -1;
    }

    if (g_client_fd != -1)
    {
        close(g_client_fd);
        g_client_fd = -1;
    }

    __LS_OUTPUT_REDIRECT_Redirect2Serial();

    return 0;
}

The H file code is as follows

#ifndef __LS_OUTPUT_REDIRECT_H__
#define __LS_OUTPUT_REDIRECT_H__

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif



/// @brief output redirection initialization
/// @param
/// @return
int LS_OUTPUT_REDIRECT_Init();



/// @brief output redirection to initialize
/// @param
/// @return
int LS_OUTPUT_REDIRECT_Deinit();



#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif


#endif

The main program calls:

The effect is as follows:

telnet port 401 of the device, the printed information will be redirected to telnet, and if telnet is disconnected, it will be redirected back to the serial port