Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a unix-socket file remains after its creator exits? Can I reuse it?

I work with unix sockets for the first time, and I stumbled upon a problem with unix socket file not getting automatically removed. It seems, after I create a socket, I can't just exit a program, instead I should remove the socket file as well.

But why the socket file doesn't get destroyed after the program that created it quits? It looks like there should be a way to connect somehow an application to this remaining file, or why is it still there?

Below is a code to illustrate (it creates a unix socket and then just exits):

#include <cstdio>
#include <sys/socket.h>
#include <sys/un.h>

int CreateSocket(const char* filename) {
    if (strlen(filename) > sizeof(sockaddr_un::sun_path)) {
        puts("Too long filename!");
        return -1;
    }
    int fdSock = socket(AF_UNIX, SOCK_DGRAM, 0);
    if(fdSock == -1){
        perror("socket");
        return -1;
    }
    sockaddr_un server;
    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, filename);
    if(bind(fdSock, (sockaddr*)&server, sizeof(sockaddr_un)) == -1){
        perror("socket");
        return -1;
    }
    return 0;
}
like image 895
Hi-Angel Avatar asked Jul 01 '14 07:07

Hi-Angel


People also ask

How do I remove a socket in UNIX?

You can remove the socket file with a simple: unlink(path); Wherever you program exit your program, check if it exists, and remove it.

Are UNIX sockets blocking?

The traditional UNIX system calls are blocking. For example: accept() blocks the caller until a connection is present. If no messages space is available at the socket to hold the message to be transmitted, then send() normally blocks.

How do UNIX sockets work?

Unix sockets are bidirectional. This means that every side can perform both read and write operations. While, FIFOs are unidirectional: it has a writer peer and a reader peer. Unix sockets create less overhead and communication is faster, than by localhost IP sockets.

Are UNIX domain sockets reliable?

Valid socket types in the UNIX domain are: SOCK_STREAM, for a stream-oriented socket; SOCK_DGRAM, for a datagram-oriented socket that preserves message boundaries (as on most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder datagrams); and (since Linux 2.6.


3 Answers

Short quote from the awesome book "The Sockets Networking API: UNIX® Network Programming Volume 1, Third Edition" by W. Richard Stevens; Bill Fenner; Andrew M. Rudoff.

Chapter 15. Example: bind of Unix Domain Socket.

Remove pathname first. The pathname that we bind to the socket is the command-line argument. But the bind will fail if the pathname already exists in the filesystem. Therefore, we call unlink to delete the pathname, in case it already exists. If it does not exist, unlink returns an error, which we ignore.

Actually, you can use the path to detect the server exists. When server terminating it have to remove a file to let the client to understand there is no server that can read incoming connections. For example Docker works this way: if the daemon not spawned there is no docker.sock file in your system.

like image 186
17 revs, 13 users 59% Avatar answered Nov 15 '22 15:11

17 revs, 13 users 59%


I cannot fully understand your questions. I think you do not understand the socket() mechanism. I will explain. Socket here is similar to pointer or file descriptor. There are two different programs: the server and the client. They are not dependent. Server opens a socket and waits for a connection. Client opens its OWN socket, connects to a remote address. Server handles the connection. Data exchange between these two programs occurs, then (if necessary) connections are terminated. Here are examples of simplest client and server programs for TCP/IP, they show how the socket() mechanism works.

Server code:

void * server (void)
{
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    socklen_t lenkeepalive;
    struct net_pack recvline;
    int i,n;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(PORT);

    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    listen(listenfd, LISTENQ);

    int keepalive =1;
    lenkeepalive = sizeof(keepalive);
    if(setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
    {
        perror("server: setsockopt()");
        shutdown(listenfd,SHUT_RDWR);
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    pthread_create (NULL, NULL, (void *)server_send, NULL);

    for(;;)
    {

        if(connfd = accept(listenfd, (SA *) NULL, NULL)>0){
            bzero(&recvline, sizeof(recvline));
            //try get msg from connected client
            while ( (recv(connfd, &recvline, MAXLINE, 0)) > 0)
            {
                printf("server: recvline.msg - %s\n",recvline.msg);
                bzero(&recvline, sizeof(recvline));
                /*
                    ...
                */
                // send answ

                encode(&recvline,"hello2",H); //make a msg

                if(send(connfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL); < 0)
                {
                    printf("server_send: send error %d (%s)\n",errno, strerror(errno));
                }

                //discard client connection
                shutdown(connfd,SHUT_RDWR);
                close(connfd);
            }

        }

    }

    // some error occurs    
    perror("server: accept()");
    shutdown(listenfd,SHUT_RDWR);
    close(listenfd);
    return 0;
}

Client code

void * client (void)
{
    socklen_t           lenkeepalive;
    int                 sockfd, keepalive, n;
    struct net_pack recvline;       //pack for msg 
    struct sockaddr_in  servaddr;

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {printf("client: socket error %d ( %s )\n ",errno, strerror(errno));}

    keepalive = 1;
    lenkeepalive = sizeof(keepalive);
    if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, lenkeepalive) < 0)
    {
        perror("client: setsockopt()");
        shutdown(sockfd,SHUT_RDWR);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(PORT);

    if (inet_pton(AF_INET, servip, &servaddr.sin_addr) <= 0)
        printf("client: inet_pton error for %s\r\n", servip);


    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("client: connect to %s error %d ( %s )\r\n",servip, errno, strerror(errno));
    }
    else
    {
        // send a msg

        encode(&recvline,"hello",H); //make a msg

        n = send(sockfd, &recvline, (strlen(recvline.msg)+4), MSG_NOSIGNAL);
        if(n < 0)
        {printf("client: i cannot send, error %d (%s)\n",errno, strerror(errno));}

        bzero(&recvline, sizeof(recvline));

        //recv data from server
        while( (n = recv(sockfd, &recvline, MAXLINE, 0)) >= 0)
            {
                printf("client: recvline.msg - %s\n",recvline.msg);
                bzero(&recvline, sizeof(recvline));
            }
    }

    // some error occurs
    perror("client: recv()");
    shutdown(sockfd,SHUT_RDWR);
    close(sockfd);

    return 0;
}

In your case you should use sockaddr_un instead of sockaddr_in, and some necessary flags like AF_UNIX, SOCK_DGRAM and other.

like image 36
nondefaultname Avatar answered Nov 15 '22 17:11

nondefaultname


Unix domain sockets operate on files so you can use all the file permission features on them etc. However if you do not care for these you actually want to use abstract unix domain sockets, by setting sun_path[0] to '\0':

abstract: an abstract socket address is distinguished (from a pathname socket) by the fact that sun_path[0] is a null byte ('\0'). The socket's address in this namespace is given by the additional bytes in sun_path that are covered by the specified length of the address structure. (Null bytes in the name have no special significance.) The name has no connection with filesystem pathnames. When the address of an abstract socket is returned, the returned addrlen is greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of the socket is contained in the first (addrlen - sizeof(sa_family_t)) bytes of sun_path.

As claimed, they do not respect the file permission operations:

Socket permissions have no meaning for abstract sockets: the process umask(2) has no effect when binding an abstract socket, and changing the ownership and permissions of the object (via fchown(2) and fchmod(2)) has no effect on the accessibility of the socket.

When you process exists you can safely reuse the identifier used in bind:

Abstract sockets automatically disappear when all open references to the socket are closed.

However mind you that this is a non-portable linux extension:

The abstract socket namespace is a nonportable Linux extension.

Source

like image 31
throws_exceptions_at_you Avatar answered Nov 15 '22 15:11

throws_exceptions_at_you