UNIX domain sockety na FreeBSD

No Comments

Po delší době jsem se pokusil pracovat s UNIX sokety na FreeBSD, a opět jsem řešil stejný problém jako už dříve. To je důvod, proč jsem se pokusil vytvořit návod jak na to.

Pokud se pokusíte jít podle běžného návodu tak narazíte na problém, který se projeví krátkým jménem soketu (přesně o jeden znak). Problém je ukryt ve struktuře sockaddr_un.

Standard pro sockaddr_un je:

struct sockaddr_un {
    sa_family_t sun_family;  /* AF_UNIX */
    char sun_path[108];      /* pathname */
};

kdežto na FreeBSD (ale třeba i na OSX) je:

struct sockaddr_un {
    unsigned char sun_len;   /* length including null */
    sa_family_t sun_family;  /* AF_LOCAL */
    char sun_path[104];      /* pathname */
};

Běžný postup je zavolat unlink() před bind(), a výpočet délky viz. níže.

len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
  // Tohle nefunguje
}

Správná cesta je …

if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
  perror("bind");
  exit(1);
}

Kompletní řešení může být takovéto:

/* 
 * File:   unixsock_server.c
 * Author: Tomas Jelinek <platanek at gmail.com>
 *
 * Created on June 29, 2013, 6:43 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>


#define SOCK_PATH  "/var/run/my_socket"
#define UX_BACKLOG 5

/*
 * 
 */
int main(int argc, char** argv) {

    
    struct sockaddr_un local;
    struct sockaddr_un remote;
    int ux_socket;
    
    /* 
     * domain   - PF_LOCAL    - Host-internal protocols, formerly called PF_UNIX
     * type     - SOCK_STREAM - Stream socket
     * protocol - 0           - default
     */
    if ((ux_socket = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) {
        perror("socket"); /* TODO handle error*/
        exit(EXIT_FAILURE);
    }

    /*
     * TODO comment
     */
    if (strlen(SOCK_PATH) >= sizeof(local.sun_path)) {
        perror("path to long!"); /* TODO handle error */
        exit(1);
    }
    
        
    /*
     * Create the address we will bind to.      
     */
    local.sun_len = sizeof(local);
    local.sun_family = AF_LOCAL;
    strcpy(local.sun_path, SOCK_PATH);
    
    /*
     * We unlink the name first, so that bind won't 
     * fail
     */
    unlink(local.sun_path);
    
    
    /*
     * Try to bind the address to the socket.
     * 
     * The third argument indicates the "length" of
     * the structure, not just the length of the 
     * socket name
     */
    
    if (bind(ux_socket, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
        perror("bind"); /* TODO  handle error*/
        exit(1);
    }
    
    
    /*
     * Listen on the socket
     */
    if (listen(ux_socket, UX_BACKLOG) == -1) {
        perror("listen"); /* TODO handle error */
        exit(1);
    }
        
    close(ux_socket);
    unlink(local.sun_path);
    
    return (EXIT_SUCCESS);
}

Tohle mne dostalo už tolikrát, tak doufám že to někomu pomůže, a ušetří nervy.

powered by EndomondoWP