segfault с in genops, в то время как pthread_create после libcurl / RTSP - PullRequest
0 голосов
/ 15 марта 2019

У меня есть следующий код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <getopt.h>
#include <stdbool.h>


#include <curl/curl.h>

#include <pthread.h>

#define BUFFER_SIZE 1024

#define SW_URL_STR_lEN 255
struct test_url {
    char protocol[SW_URL_STR_lEN];
    char hostname[SW_URL_STR_lEN];
    char page[SW_URL_STR_lEN];
    int port;
};

typedef struct test_url test_url_t;
test_url_t *test_create_url();
void test_destroy_url(test_url_t *url);
bool test_parse_url(test_url_t *url, const char *to_parse);



static int _getch(void) {
    struct termios oldt, newt;
    int ch;

    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

    return ch;
}

static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
    (void) ptr;

    if (size * nmemb > BUFFER_SIZE) {
        fprintf(stdout, "Error buffer too small");
        exit(EXIT_FAILURE);
    }

    char *buf = (char *) userdata;

    strncpy(buf, ptr, size * nmemb);

    return size * nmemb;
}

static void send_rtsp_options_request(CURL *handle, const char *uri) {
    fprintf(stdout, "\nRTSP: OPTIONS %s\n", uri);
    curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
    curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
    curl_easy_perform(handle);
}

static void send_rtsp_describe_request(CURL *handle, const char *uri, char *str_sdp) {
    fprintf(stdout, "\nRTSP: DESCRIBE %s\n", uri);

    fprintf(stdout, " describe: %s", str_sdp);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, str_sdp);
    curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
    curl_easy_perform(handle);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, stdout);
}

static void send_rtsp_setup_request(CURL *handle, const char *uri, const char *transport) {
    fprintf(stdout, "\nRTSP: SETUP %s\n", uri);
    fprintf(stdout, "      TRANSPORT %s\n", transport);
    curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
    curl_easy_setopt(handle, CURLOPT_RTSP_TRANSPORT, transport);
    curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
    curl_easy_perform(handle);
}

static void send_rtsp_play_request(CURL *handle, const char *uri, const char *range) {
    fprintf(stdout, "\nRTSP: PLAY %s\n", uri);
    curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
    curl_easy_setopt(handle, CURLOPT_RANGE, range);
    curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
    curl_easy_perform(handle);

    /* Disable ranges */
    curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
}

// static void send_rtsp_pause_request(CURL *handken, const char *uri, const char *range) {
//  fprintf(stdout, "\nRTSP: PAUSE %s\n", uri);
//  curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
//     curl_easy_setopt(handle, CURLOPT_RANGE, range);
//     curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PAUSE);
//     curl_easy_perform(handle);

//     /* Disable ranges */
//     curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
// }

static void send_rtsp_teardown_request(CURL *handle, const char *uri) {
    fprintf(stdout, "\nRTSP: TEARDOWN %s\n", uri);
    curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
    curl_easy_perform(handle);
}

/* Main options */
typedef enum {
    rtsp_injector_arg_help       =     'h',
    rtsp_injector_arg_addr       =     'a',
    rtsp_injector_arg_verbose    =     'v',
} rtsp_injector_arg_t;

static void rtsp_print_help() {
    fprintf(stdout, "Usage: \n");
    fprintf(stdout, "\t-%c, --help\n", rtsp_injector_arg_help);
    fprintf(stdout, "\t\tprints this help, and exits\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "\t-%c, --rtsp-addr\n", rtsp_injector_arg_addr);
    fprintf(stdout, "\t\tThe rtsp address of the server, with the port and the right camera choice such as \
                            \n\t\tActive camera - 'rtsp://(device ip address):5554/camera' \
                            \n\t\tBack camera - 'rtsp://(device ip address):5554/back' \
                            \n\t\tFront camera - 'rtsp://(device ip address):5554/front'\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "\t-%c, --verbose\n", rtsp_injector_arg_verbose);
    fprintf(stdout, "\t\tprints more details in the packets trading\n");
    fprintf(stdout, "\n");
}

static void *pthread_proc(void *arg) {
    (void)arg;
    fprintf(stdout, "COIN COIN\n");
    return NULL;
}


int main(int argc, char **argv) {
    if (argc < 2) {
        rtsp_print_help();
        return EXIT_FAILURE;
    }

    static struct option long_options[] = {
        {"help",        no_argument,        NULL,    rtsp_injector_arg_help       },
        {"rtsp-addr",   required_argument,  NULL,    rtsp_injector_arg_addr       },
        {"verbose",     no_argument,        NULL,    rtsp_injector_arg_verbose    },
        { NULL,         0,                  NULL,    0                            },
    };

    int option_index = 0;
    int c = 0;
    char opstring[1024] = {0};
    size_t s = 0;
    pthread_t thrd;
//    pthread_create(&thrd, NULL, pthread_proc, NULL);
    s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_help);
    s += snprintf(opstring + s, sizeof(opstring) - s, "%c:", rtsp_injector_arg_addr);
    s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_verbose);

    long verbose_is_on = 0L;
    char *url = NULL;

    while ((c = getopt_long(argc, argv, opstring, long_options, &option_index)) != -1) {
        switch (c) {
            case rtsp_injector_arg_help:
                rtsp_print_help();
                return EXIT_SUCCESS;
            case rtsp_injector_arg_addr:
                url = optarg;
                break;
            case rtsp_injector_arg_verbose:
                verbose_is_on = 1L;
                break;
            default:
                fprintf(stdout, "Invalid option 0%o: '%c' ??\n", c, c);
                rtsp_print_help();
                return EXIT_FAILURE;
        }
    }

    if (url == NULL) {
        fprintf(stdout, "Missing mandatory prefix '-a' to the server address\n");
        rtsp_print_help();
        return EXIT_FAILURE;
    }

    int res = 0;

    /* Where the PLAY starts and when should it ends */
    const char *range = "0.000-";
    char uri[1024] = {0};
//     char control[1024] = {0};
    char str_sdp[BUFFER_SIZE] = {0};

    /* Initialize all libcurl sub moduless */
    res = curl_global_init(CURL_GLOBAL_ALL);
    if (res != CURLE_OK) {
        fprintf(stdout, "curl_global_init failed\n");
        return EXIT_FAILURE;
    }
    /* Create a sesion handle */
    CURL *my_handle = curl_easy_init();
    if (my_handle == NULL) {
        fprintf(stdout, "curl_easy_init failed\n");
        return EXIT_FAILURE;
    }

    /* Set the url to work with, must to be the right format ie RFC , the function doesn't check it */
    curl_easy_setopt(my_handle, CURLOPT_URL, url);

    /* Parse server information ie ip and port */
    test_url_t *my_url = test_create_url();
    test_parse_url(my_url, url);

    fprintf(stdout, "Protocol: %s\nHostname: %s\nPort: %d\nPage: %s\n",
            my_url->protocol, my_url->hostname, my_url->port, my_url->page);

    curl_easy_setopt(my_handle, CURLOPT_NOPROGRESS, 1L);
    curl_easy_setopt(my_handle, CURLOPT_HEADERDATA, stdout);

    curl_easy_setopt(my_handle, CURLOPT_USERAGENT, "test-client-rtsp");

    curl_easy_setopt(my_handle, CURLOPT_VERBOSE, verbose_is_on);

    /* Let's send some requests */
    snprintf(uri, sizeof(uri), "%s", url);

    /* OPTIONS */
    send_rtsp_options_request(my_handle, uri);

    /* DESCRIBE */
    send_rtsp_describe_request(my_handle, uri, str_sdp);
    fprintf(stdout, "Sdp information:\n%s", str_sdp);


    // char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
    char transport[1024] = "RTP/AVP;unicast;client_port=1234-1235";
    fprintf(stdout, "Transport: %s", transport);

    /* SETUP */
    snprintf(uri, sizeof(uri), "%s", url);
    strncat(uri, "/trackID=0", strlen(uri)); /* Different url for SETUP, check doc in trac with the scenari exemple */
    send_rtsp_setup_request(my_handle, uri, transport);

    /* PLAY */
    snprintf(uri, sizeof(uri), "%s", url);
    send_rtsp_play_request(my_handle, uri, range);
    pthread_create(&thrd, NULL, pthread_proc, NULL);
    _getch();

    /* TEARDOWN */
    send_rtsp_teardown_request(my_handle, uri);

    /* Destroy the session handle */
    curl_easy_cleanup(my_handle);

    /* Cleanup curl init resources */
    curl_global_cleanup();
    test_destroy_url(my_url);

    return EXIT_SUCCESS;
}




test_url_t * test_create_url() {
    test_url_t *ret = NULL;

    ret = malloc(sizeof(test_url_t));
    if (!ret) {
        return NULL;
    }
    memset(ret, 0, sizeof(test_url_t));

    return ret;
}

void test_destroy_url(test_url_t *url) {
    free(url);
}

bool test_parse_url(test_url_t *url, const char *to_parse) {
    int pos_of_hostname = 0, pos_port = 0, pos_page = 0;
    const char *hostname = NULL;
    const char *str_port = NULL;
    const char *str_page = NULL;
    char str_port_number[4];

    if (to_parse == NULL) {
        return false;
    }

    hostname = strstr(to_parse, "://");
    if (hostname == NULL) {
        return false;
    }

    pos_of_hostname = hostname - to_parse; /* Get the position of "://" */
    if (pos_of_hostname > (int)sizeof(url->protocol)) {
        return false;
    }
    strncpy(url->protocol, to_parse, sizeof(url->protocol));
    url->protocol[pos_of_hostname] = '\0';

    hostname = to_parse + pos_of_hostname + 3; /* Skip protocol and '://' */

    /* Check if port is provided */
    str_port = strstr(hostname, ":");
    if (str_port != NULL) {
        pos_port = str_port - hostname;
        strncpy(url->hostname, hostname, sizeof(url->hostname));
        url->hostname[pos_port] = '\0';
        str_page = strstr(hostname, "/");
        if (str_page != NULL) { /* Check if page is provided */
            pos_page = str_page - hostname;
            str_page = hostname + pos_page + 1;
            strncpy(url->page, str_page, sizeof(url->page));
            if (pos_port < pos_page) { /* Check if ':' is before '/' ex: ':8080/something' */
                strncpy(url->hostname, hostname, sizeof(url->hostname));
                url->hostname[pos_port] = '\0';
                /* Port number is provided */
                str_page = hostname + pos_port + 1; /* Skip hostname and the ':' before port number' */
                strncpy(str_port_number, str_page, sizeof(str_port_number));
                url->port = atoi(str_port_number);
            }
            else { /* Page provided before port */
                /* Warning: strncpy doesn't put a '\0' at the end of the string if src doesn't have one*/
                strncpy(url->hostname, hostname, sizeof(url->hostname));
                url->hostname[pos_page] = '\0';
            }
        }
        else { /* There is no page provided */
            str_page = hostname + pos_port + 1;
                strncpy(str_port_number, str_page, sizeof(str_port_number));
                url->port = atoi(str_port_number);
        }
    }
    else { /* Only page provided */
        str_page = strstr(hostname, "/");
        if (str_page != NULL) { /* Check if page is provided */
            pos_page = str_page - hostname;
            str_page = hostname + pos_page + 1;
            strncpy(url->page, str_page, sizeof(url->page));
            url->page[strlen(str_page)] = '\0';
            strncpy(url->hostname, hostname, sizeof(url->hostname));
            url->hostname[pos_page] = '\0';
        }
        else { /* Case where there is only a hostname provided */
            strncpy(url->hostname, hostname, sizeof(url->hostname));
        }
    }

    return true;
}

Код падает в строке 256 pthread_create со следующей обратной трассировкой:

Program received signal SIGSEGV, Segmentation fault.
__GI__IO_enable_locks () at genops.c:552
552     genops.c: No such file or directory.
(gdb) bt
#0  __GI__IO_enable_locks () at genops.c:552
#1  0x00007ffff7bbe52d in __pthread_create_2_1 (newthread=<optimized out>, attr=0x0, start_routine=<optimized out>, arg=0x0) at pthread_create.c:736
#2  0x0000555555557db5 in main (argc=3, argv=0x7fffffffd988) at test_client_rtsp.c:256

Если я раскомментирую первый pthread_create и прокомментирую второй, он работает нормально. Я так понимаю, это связано с libcurl, но я не слишком уверен в этом.

Curl 7.64.0 компилируется с:

./configure --disable-shared --enable-static --disable-ldap --disable-sspi --without-librtmp --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb --without-libidn --without-zlib --without-ssl --enable-rtsp

Я немного растерялся относительно того, что там происходит.

...