beeef beeef - 4 months ago 11
C Question

C Segmentation Fault (Libcapn APNS Library)

I'm working on a C program which uses the Libcapn Library (http://libcapn.org) to connect to Apple's Push Notification Service and send push notifications.

The example which is shown on the website works fine, I can send push notifications to my iPhone with that.

Now I'm going to integrate this into my existing program to send more specific notifications to my iPhone, but I always get a segmentation fault when I try to split the code into multiple functions.

Example: In the given code everything happens in the

main
function:

int main() {
apn_payload_t *payload = NULL;
apn_ctx_t *ctx = NULL;
time_t time_now = 0;
char *invalid_token = NULL;

// ...

if(NULL == (ctx = apn_init())) {
printf("Unable to init context: %d\n", errno);
apn_library_free();
return -1;
}

apn_set_pkcs12_file(ctx, "my-certificate.p12", "my4711passphrase");
apn_set_mode(ctx, APN_MODE_SANDBOX); //APN_MODE_PRODUCTION or APN_MODE_SANDBOX
apn_set_behavior(ctx, APN_OPTION_RECONNECT);
apn_set_log_level(ctx, APN_LOG_LEVEL_INFO | APN_LOG_LEVEL_ERROR | APN_LOG_LEVEL_DEBUG);
apn_set_log_callback(ctx, __apn_logging);
apn_set_invalid_token_callback(ctx, __apn_invalid_token);

// ...

}


Although it works it's not exactly what I want. I don't want to allocate/free all resources everytime I want to send a single notification. Therefore I want to open the connection at startup, set the mode to "RECONNECT" (so it reconnects automatically after the connection was closed) and just free all resources when the program terminates.

So if I just "outsource" e.g. the initialization of the
context
to a new function, I get a segmentation fault. Here's what I did:

int apn_ctx_init_wrapper(apn_ctx_t *ctx) {
if(NULL == (ctx = apn_init())) {
printf("Unable to init context: %d\n", errno);
apn_library_free();
return -1;
}

apn_set_pkcs12_file(ctx, "my-certificate.p12", "my4711passphrase");
apn_set_mode(ctx, APN_MODE_SANDBOX); //APN_MODE_PRODUCTION or APN_MODE_SANDBOX
apn_set_behavior(ctx, APN_OPTION_RECONNECT);
apn_set_log_level(ctx, APN_LOG_LEVEL_INFO | APN_LOG_LEVEL_ERROR | APN_LOG_LEVEL_DEBUG);
apn_set_log_callback(ctx, __apn_logging);
apn_set_invalid_token_callback(ctx, __apn_invalid_token);

return 0;
}


... and I replaced the code above with to a single function call like
apn_ctx_init_wrapper(ctx);
. It doesn't even return
0
or
-1
. When the function get's called I get a segfault. Why? Please help me!

Answer

This is a classic case of passing a pointer by value and attempting to point the local copy of the pointer to a new location. The original pointer is left pointing at the original location.

I will give you 2 approaches to this situation.

You can modify the function so it will accept the address of the pointer.

int apn_ctx_init_wrapper(apn_ctx_t **ctx) {
    if(NULL == (*ctx = apn_init())) { //Note that the pointer needs to be dereferenced
        printf("Unable to init context: %d\n", errno);
        apn_library_free();
        return -1;
    }

    apn_set_pkcs12_file(*ctx, "my-certificate.p12", "my4711passphrase");
    apn_set_mode(*ctx,  APN_MODE_SANDBOX); //APN_MODE_PRODUCTION or APN_MODE_SANDBOX
    apn_set_behavior(*ctx, APN_OPTION_RECONNECT);
    apn_set_log_level(*ctx, APN_LOG_LEVEL_INFO | APN_LOG_LEVEL_ERROR | APN_LOG_LEVEL_DEBUG);
    apn_set_log_callback(*ctx, __apn_logging);
    apn_set_invalid_token_callback(*ctx, __apn_invalid_token);

    return 0;
}

And in main you can call it as if(apn_ctx_init_wrapper(&ctx) != -1).

Second method is to return the context instead of returning 0 or -1.

apn_ctx_t *apn_ctx_init_wrapper() {
    apn_ctx_t *ctx;
    if(NULL == (ctx = apn_init())) {
        printf("Unable to init context: %d\n", errno);
        apn_library_free();
        return NULL;
    }

    apn_set_pkcs12_file(ctx, "my-certificate.p12", "my4711passphrase");
    apn_set_mode(ctx,  APN_MODE_SANDBOX); //APN_MODE_PRODUCTION or APN_MODE_SANDBOX
    apn_set_behavior(ctx, APN_OPTION_RECONNECT);
    apn_set_log_level(ctx, APN_LOG_LEVEL_INFO | APN_LOG_LEVEL_ERROR | APN_LOG_LEVEL_DEBUG);
apn_set_log_callback(ctx, __apn_logging);
apn_set_invalid_token_callback(ctx, __apn_invalid_token);

    return ctx;
}

An in main you can call it as if(NULL == (ctx = apn_ctx_init_wrapper())). There is no need to pass context into the function as the function is creating and initializing the context.

I prefer the second method.

Comments