UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

  • Index
  • » Networking
  • » problems with unix named socket. How to send command outputs remotely

#1 2012-07-12 03:32 PM

occam25
Member
Registered: 2012-07-12
Posts: 4

problems with unix named socket. How to send command outputs remotely

Hi!
I am having some problems with an AF_UNIX named socket. 
I have programmed a server that accepts the conexion of a client to manage the system remotely. In some cases, the client sends an order to execute some standar commands in the server and I want the server to send back to the client the outputs of that commands.

I am not sure if there is a better way to do it, but this is what I did:

1. The server creates a local named socket (/tmp/named_socket) when it starts and two inet sockets (one to comunicate with the client and another one to send the output of the command executed. The client connects to both sockets and prints all that it receives in this "command socket" to an output window) <- I could use the same socket but then I would have to parse the information in order to know what is the output of a command and what is not, so I think it is easier in this way.

2. In the server, one thread is dedicated to read the local named socket and sends to the client all it reads through the "command socket".

3. I execute the commands redirecting the output to another program I wrote (called "named_redirect") that takes what it receives from the stdin and writes it to the named socket. For example: if I execute 'ls /home | named_redirect" the output of the ls command is written to the /tmp/named_socket and then, the server thread will read it and send it to the client

This all works fine, but sometimes (i don't know why) the named_redirect program stops working and the client stops receiving command outputs. It stops working sometimes after one day sometimes after one week.. and I don't know how to force the error so it is very dificult to debug

I show here some of the code, if you see something wrong or think there is a better way to do it please tell me! :)

++++++ named_redirect code +++++++++++++++++++++++

#define NAMED_SOCKET    "/tmp/named_socket"

int main(int argc, char * argv[]){

   struct sockaddr_un name;
   size_t size;
   char cadena[1024];
   int n;

   /* Create the socket. */
   sock = socket(AF_UNIX, SOCK_DGRAM, 0); 
   if (sock < 0){ 
      perror ("socket");
      exit(EXIT_FAILURE);
   }   
   name.sun_family = AF_UNIX;
   strncpy(name.sun_path, NAMED_SOCKET, sizeof(name.sun_path));
   size = (offsetof(struct sockaddr_un, sun_path) + strlen(name.sun_path) + 1); 
   while(1){
      memset(cadena,'\0',sizeof(cadena));
      // read stdin
      n = read(0, cadena, sizeof(cadena));
      if(n < 0){ 
         perror("error al leer stdin");
	 close(sock);
         exit(1);
      }else if(n > 0){
         // send to named socket
         if(sendto(sock, cadena, strlen(cadena), 0, (struct sockaddr *)&name, sizeof(struct sockaddr_un)) < 0){ 
            perror("error en write");
	    close(sock);
            exit(1);
         }         
      }else{
	 close(sock);
         exit(0);
      }   
   }   
   close(sock);
   return 0;
}

++++++++++++++++ in server code ++++++++++++++++++++++++++

/////////// create the named socket at startup ///////////////////////

    unlink("/tmp/named_socket");

   /* Create the socket. */
   sock = socket(PF_UNIX, SOCK_DGRAM, 0);

   if (sock < 0){
      perror ("socket local");
      return -1;
   }

   /* Bind a name to the socket. */
   name.sun_family = AF_FILE;
   strncpy (name.sun_path, filename, sizeof(name.sun_path));

   /* The size of the address is
      the offset of the start of the filename,
      plus its length,
      plus one for the terminating null byte. */
   size = (offsetof(struct sockaddr_un, sun_path) + strlen(name.sun_path) + 1);

   if(bind(sock, (struct sockaddr *) &name, size) < 0)
   {
      perror ("bindeo socket local");
      return -1;
   }
.
.
// (somewhere in the code a dedicated thread keeps reading the named socket and sending the data to the client)
.
.
/////// close named socket before exit ///////////////
   close(sck_local);
   unlink("/tmp/named_socket");

any help or suggestion will be apreciate!

thanks

Last edited by occam25 (2012-07-12 03:38 PM)

Offline

#2 2012-07-12 08:33 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: problems with unix named socket. How to send command outputs remotely

I'm unclear on the need for the AF_UNIX socket?  You seem to just use it for filtering the command output through?  Why not simply send the command output directly to the client AF_INET socket, without any need for the "named_redirect" command at all?  Ie: when the server executes a command (eg: "ls"), have the server simply read that command's output and directly send it, rather than bother reading the Unix domain socket for the output...  You can use something like popen() to easily read the command's output...  Or, you can roll your own with fork() + dup2()'ing one end of a pipe()/socketpair() over stdout...  But, either way, it's a hell of a lot less complicated than needing a Unix domain socket and an entire separate program to pipe output through!

But, as for the problem...  When things stop working, what is the state of the system?  Are there any running/hung instances of your "named_redirect" program still running?  Is the server thread that's supposed to read the Unix domain socket still running properly?

Offline

#3 2012-07-12 10:12 PM

occam25
Member
Registered: 2012-07-12
Posts: 4

Re: problems with unix named socket. How to send command outputs remotely

Hi RobSeace, thanks for the answer. I choosed that solution because in this way the client can see the output of any command executed not only by the server but also by any program or script. Imagine, for example, that a job is executed by cron, I can execute it redirecting the output to the named_socket in a very simple way and it will appear in the client side.

When is the server who executes the command I do it like this:

// this function is called to execute the command. Eg: command_execute("ls;/etc;-lh");
void command_execute(char *command){

    pid_t pid;

    pid = fork();

    if(pid == 0){
        if(verbose > 2) fprintf(stderr,"Executing: %s\n", command);

        daemonize(); //daemonize process

        docommand2socket(command); //execute command to named socket
        exit(0);
    }else if(pid < 0){
        if(!daemon_mode) fprintf(stderr,"Error en el fork de command_execute\n");
    }
}

void docommand2socket(char *command)
{

    int i;
    char delims[] = ";";
    char *result = NULL;
    char *parameters[20];
    char *index;
    pid_t pid;

    // parse parameters by ';'
    i=0;
    result = strtok(command, delims);
    while((result != NULL)&&(i < 19))
    {
        *(parameters + i) = strdup(result);
        index = strchr(parameters[i],'\n');
        if(index != NULL) *index = '\0';
        result = strtok(NULL, delims);
        i++;
    }
    parameters[i] = (char *)NULL;
    int pipefd[2];

    /* get a pipe (buffer and fd pair) from the OS */

    if (pipe(pipefd)) {
        perror("pipe");
        return;
    }
    pid = fork();

    switch(pid) {
    case -1:
        perror("fork");
        return;
    case 0:
        /* child */

        close(pipefd[0]);  /* the other side of the pipe */
        dup2(pipefd[1], 1);  /* automatically closes previous fd 1 */
        close(pipefd[1]);  /* cleanup */

        execvp(parameters[0], parameters);
        perror("primer execv de docommand2socket");
        exit(1);
    default:
        /* parent */

        close(pipefd[1]);  /* the other side of the pipe */
        dup2(pipefd[0], 0);  /* automatically closes previous fd 0 */
        close(pipefd[0]);  /* cleanup */

        /* exec */
        execl(ns_redirect, ns_redirect, (char *)0);
        perror("exec en docommand2socket para stdin_connect_named_socket");
        exit(1);
    }
}

is this the way you said?

Offline

#4 2012-07-13 12:39 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: problems with unix named socket. How to send command outputs remotely

Offline

#5 2012-07-13 06:19 PM

occam25
Member
Registered: 2012-07-12
Posts: 4

Re: problems with unix named socket. How to send command outputs remotely

void command_execute(char *command){
    pid_t pid;

    pid = fork();

    if(pid == 0){

        if(verbose > 2) fprintf(stderr,"Executing: %s\n", command);
        docommand2socket(command);
        exit(0);
    }else if(pid < 0){
        if(!daemon_mode) fprintf(stderr,"Error en el fork de command_execute\n");
    }
}

void docommand2socket(char *command)
{

    int i;
    char delims[] = ";";
    char *result = NULL;
    char *parameters[20];
    char *index;
    pid_t pid;

    int ret;
    struct client *cli_index;
    struct s_server *ss;
    char buffer[1024];

    ss = &_server;

    // Separo por delimitador
    i=0;
    result = strtok(command, delims);
    while((result != NULL)&&(i < 19))
    {
        *(parameters + i) = strdup(result);
        index = strchr(parameters[i],'\n');
        if(index != NULL) *index = '\0';
        result = strtok(NULL, delims);
        i++;
    }
    parameters[i] = (char *)NULL;
    int pipefd[2];

    /* get a pipe (buffer and fd pair) from the OS */

    if (pipe(pipefd)) {
        perror("pipe");
        return;
    }

    /* We are the child process, that will send the data to the clients (to let the main process free, because we don't know how long the
	execution of the command will be), and we need another process to execute the command, so we fork again*/

    pid = fork();

    switch(pid) {
    case -1:
        perror("fork");
        return;
    case 0:
        /* child */
        /* do redirections and close the wrong side of the pipe */

        close(pipefd[0]);  /* the other side of the pipe */
        dup2(pipefd[1], 1);  /* automatically closes previous fd 1 */
        dup2(pipefd[1], 2); // also for stderr
	
        execvp(parameters[0], parameters);
        perror("primer execv de docommand2socket");
        exit(1);
    default:
        /* parent */
        /* do redirections and close the wrong side of the pipe */

        close(pipefd[1]);  /* the other side of the pipe */
        dup2(pipefd[0], 0);  /* automatically closes previous fd 0 */

        memset(buffer,'\0',sizeof(buffer));
        while((ret = read(pipefd[0], buffer, sizeof(buffer))) > 0)
        {
            // if there is any client, send data to him
            if(ss->server_clients->next != NULL)
            {
                cli_index = ss->server_clients->next;
                while(cli_index != NULL)
                {
                    if((cli_index->authenticated == 1)&&(cli_index->scriptout_fd > 0))
                    {
                        send(cli_index->scriptout_fd, buffer, strlen(buffer), 0);
                    }
                    cli_index = cli_index->next;
                }
            }
            memset(buffer,'\0',sizeof(buffer));
        }
        if(ret < 0)
        {
            if(!daemon_mode) perror("Error leyendo socket local");
            exit(1);
        }
        if(!daemon_mode) fprintf(stderr,"DEBUG: Command executed\n");

        // clean up
        i=0;
        while((parameters[i] != NULL)&&(i < 19))
        {
            free(parameters[i]);
            parameters[i] = NULL;
            i++;
        }
        exit(0);
    }
}

Last edited by occam25 (2012-07-13 06:29 PM)

Offline

#6 2012-07-13 07:25 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: problems with unix named socket. How to send command outputs remotely

send(cli_index->scriptout_fd, buffer, strlen(buffer), 0);

That strlen() isn't a good idea...  You pass sizeof(buffer) to read(), so there may be no terminating null in the buffer, despite your memset()'s...

You already have the return value from read() in "ret", so just pass that as the amount of data to send()...  And, then you can lose all the pointless memset()'s, too...

Offline

#7 2012-07-13 07:37 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: problems with unix named socket. How to send command outputs remotely

static void reaper (int sig)
{
    while (waitpid (-1, NULL, WNOHANG) > 0)
        ;   /* don't hang, if no kids are dead yet */
}

...

signal (SIGCHLD, reaper);

Offline

#8 2012-07-17 06:03 PM

occam25
Member
Registered: 2012-07-12
Posts: 4

Re: problems with unix named socket. How to send command outputs remotely

send(cli_index->scriptout_fd, buffer, strlen(buffer), 0);

Last edited by occam25 (2012-07-17 06:06 PM)

Offline

#9 2012-07-17 08:09 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: problems with unix named socket. How to send command outputs remotely

Offline

  • Index
  • » Networking
  • » problems with unix named socket. How to send command outputs remotely

Board footer

Powered by FluxBB